Using RedirectView in urls.py
Posted on Sat 30 January 2016 in Web Development
Django's RedirectView is a generic Class-based View (or CBV for short) that is handy whenever you need to implement redirects. It's especially handy in urls.py
for simple use cases where you don't need conditional redirects or any other business logic. In this post, I will explore a few use cases where you can use the view directly in the urls.py
module.
A simple example
Suppose that we are revamping some areas of our product and we decide to create the new views from scratch. We want to be able to incrementally ship the new experience, while still keeping the old one around. At the end, we want to switch over to the new experience, without affecting the customers that still use the old URLs (ie. bookmarks, links sent in marketing emails, e.t.c.)
We can easily accomplish that by using RedirectView
. Here's an example.
# urls.py
from django.conf.urls import url
from django.views.generic.base import RedirectView
from myapp.views import NewExperienceView
urlpatterns = [
url(r'^path/$',
RedirectView.as_view(pattern_name='new-path', permanent=True),
name='path'),
url(r'^path/new/$',
NewExperienceView.as_view(),
name='new-path')
]
It's as easy as that. We use RedirectView.as_view
to create a new view function that we register with the url resolver. We set the pattern_name
attribute to instruct it to redirect to that named url.
Note that we also used permanent=True
to make the redirect permanent (301 Moved Permanently
instead of 302 Found
). We need this in our case, because this is a permanent switch and at some point we will remove the old URL rules. This is also a recomended practice by the search engines.
A slightly more complex example
What if our old view had required arguments encoded in the URL pattern? Will the approach presented above work?
It will, but only if the new view accepts the same arguments.
urlpatterns = [
url(r'^path/to/(?P<object_slug>[\w-]+)/$',
RedirectView.as_view(pattern_name='new-path', permanent=True),
name='path'),
url(r'^path/new/(?P<object_slug>[\w-]+)/$',
NewExperienceView.as_view(),
name='new-path')
]
An even more complex example
What if the new view doesn't have required arguments? In that case you can't use the above approach, or you'll get a 410 Gone
response.
That's happening because RedirectView
will try to use reverse
on the provided pattern_name
passing the same URL arguments extracted from your URL pattern. If the new view doesn't have arguments, the reverse will fail and you'll get the 410 Gone
response (which is the standard Django response for a URL that is None
).
How can we fix that? We can set the url
directly and use reverse_lazy
to get the actual URL of the new view.
# urls.py
from django.conf.urls import url
from django.core.urlresolvers import reverse_lazy
from django.views.generic.base import RedirectView
from myapp.views import NewExperienceView
urlpatterns = [
url(r'^path/to/(?P<object_slug>[\w-]+)/$',
RedirectView.as_view(url=reverse_lazy('new-path'), permanent=True),
name='path-to-object-slug'),
url(r'^path/new/$',
NewExperienceView.as_view(),
name='new-path')
]
We used reverse_lazy to get the actual URL for the new view and set that as the url
attribute on the RedirectView. If url
is set, RedirectView
will not do a reverse, but instead will try to do a dictionary-style string formatting, passing any arguments that where captured in the URL. In our case, the URL doesn't have any string formatting arguments, so it will remain as it is and the redirect will be handled correctly.
Other use cases
For any other use cases you most probably won't be able to use the RedirectView.as_view
directly. Here are a few examples:
- Your new view has different arguments than the old one
- You need to conditionally redirect based on the logged-in user or some other rules
- You need to perform an operation on redirect (ie. increase an access counter)
In these cases you can always subclass RedirectView
and provide a custom get_redirect_url()
method. See Django docs on RedirectView for more details on how to do that.