I have a site whose URLs look like
http://www.example.com/NY-2010/
http://www.example.com/NY-2010/location/
http://www.example.com/NY-2010/something-else/
http://www.example.com/Washington-2009/
and so on. There are various pages (like location) for various editions (like NY). I use URLconfs like
url(r'^(?P<edition>[\d]+\-[\w]+)/$', views.home),
url(r'^(?P<edition>[\d]+\-[\w]+)/location/$', views.location),
In each of the views I have to fetch the current edition. The fact is, if the edition name is wrong, I want to redirect to the latest edition. So I do something like
def home(request, edition):
try:
event = Edition.objects.get(name=edition)
except ObjectDoesNotExist:
return redirect(home, edition=Edition.latest())
# If event was found I go on here
def location(request, edition):
try:
event = Edition.objects.get(name=edition)
except ObjectDoesNotExist:
return redirect(home, edition=Edition.latest())
# If event was found I go on here
and so on. Of course there is some duplication that I’d like to minimize. I can think of two ways:
- use
get_objects_or_404()and customize the 404 view, or - abstract the common part in a function.
The problem with both ways is that they do not allow me to do a proper redirect, that is, the URL will remain the same even if the view was changed. Is there a better way to handle these redirects?
EDIT It seems my question is not clear. In particular it is not clear what I mean by abstract the common part in a function. So, what I could do is the following
def get_edition_or_current(edition):
try:
event = Edition.objects.get(name=edition)
except ObjectDoesNotExist:
event = Edition.latest()
return event
def home(request, edition):
event = get_edition_or_current(edition)
# I go on with a valid event here
def location(request, edition):
event = get_edition_or_current(edition)
# I go on with a valid event here
In this way I can display the view for a proper event, but I cannot change the URL. To change the URL, the view must return a redirect. I cannot set the return value for the view from inside get_edition_or_current.
So, how does Django implements get_object_or_404? Well, it is simple, it raises an Http404 exception, and catches it later. But of course this only works for Http404 exceptions, because Django is instructed to catch them.
I think the simplest way to do this would be to create new utility function called
get_object_or_redirectin the same vein asget_object_or_404. You could probably even copy the contents ofget_object_or_404from django.shortcuts as a starting point for your implementation, or just extract out what you have above.EDIT: as noted in the comments, a redirect cannot be done via raising an “exception,” so this really can’t work the same as get_object_or_404.