Skip to main content

Django URL Reverse

Introductionā€‹

When building Django applications, you'll often need to include URLs in your views, templates, and even other parts of your code. While you could hardcode these URLs (e.g., /users/profile/25/), it's not a sustainable approach. If you ever change your URL structure, you'd need to update every hardcoded URL throughout your project.

Django solves this problem with URL reverse - a powerful technique that allows you to reference URLs by their name rather than their actual path. URL reverse dynamically generates URLs based on the URL patterns you've defined in your urls.py files.

In this guide, you'll learn:

  • What URL reverse is and why it's important
  • How to use Django's reverse() and reverse_lazy() functions
  • Working with URL namespaces
  • Using URL reverse in templates and redirects

Why Use URL Reverse?ā€‹

Let's illustrate the problem URL reverse solves with a simple example:

Without URL reverse (problematic approach):

python
# views.py
def profile_view(request):
# Hardcoded URL šŸ˜¬
return redirect('/users/profile/')

# template.html
<a href="/users/profile/">View Profile</a>

What happens if you decide to change your URL structure to /accounts/profile/? You'd need to find and update every instance of the hardcoded URL throughout your project.

With URL reverse (recommended approach):

python
# views.py
from django.urls import reverse
from django.shortcuts import redirect

def profile_view(request):
# Using URL reverse šŸ‘
return redirect(reverse('profile'))

# template.html
<a href="{% url 'profile' %}">View Profile</a>

If you change the URL pattern, as long as the name remains the same, all your reverse lookups will automatically point to the new URL!

Basic URL Reverseā€‹

Setting Up Named URL Patternsā€‹

The first step is to assign names to your URL patterns in your urls.py file:

python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
path('blog/', views.blog_list, name='blog_list'),
path('blog/<int:post_id>/', views.blog_detail, name='blog_detail'),
path('about/', views.about, name='about'),
]

In this example, we've given names to three URL patterns: blog_list, blog_detail, and about.

Using reverse() in Python Codeā€‹

Now that we have named URL patterns, we can use the reverse() function to generate URLs:

python
from django.urls import reverse
from django.http import HttpResponseRedirect

def some_view(request):
# Generate URL for blog_list
blog_url = reverse('blog_list') # Returns '/blog/'

# Generate URL for blog_detail with parameters
post_url = reverse('blog_detail', args=[42]) # Returns '/blog/42/'

# Alternative using kwargs
post_url = reverse('blog_detail', kwargs={'post_id': 42}) # Returns '/blog/42/'

return HttpResponseRedirect(blog_url)

Using reverse_lazy()ā€‹

Sometimes you need to use a reversed URL in a context where the URL configuration isn't loaded yet, like in class attributes or module-level constants. For these cases, Django provides reverse_lazy():

python
from django.urls import reverse_lazy
from django.views.generic import CreateView
from .models import BlogPost

class BlogPostCreate(CreateView):
model = BlogPost
fields = ['title', 'content']
# URL to redirect to after successful form submission
success_url = reverse_lazy('blog_list') # Lazily evaluated when needed

URL Reverse with Parametersā€‹

Most real-world URLs include parameters. Let's see how to handle them with URL reverse:

URL Patterns with Parametersā€‹

python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
path('users/<int:user_id>/', views.user_profile, name='user_profile'),
path('category/<slug:category_slug>/posts/', views.category_posts, name='category_posts'),
]

Reversing URLs with Parametersā€‹

python
from django.urls import reverse

# Using positional arguments
user_url = reverse('user_profile', args=[5]) # Returns '/users/5/'

# Using keyword arguments
category_url = reverse('category_posts', kwargs={'category_slug': 'django-tips'})
# Returns '/category/django-tips/posts/'

URL Namespacesā€‹

As your Django project grows, you may have multiple apps with similar URL names. URL namespaces help avoid name collisions by prefixing URL names with an application namespace.

Defining URL Namespacesā€‹

python
# main urls.py
from django.urls import path, include

urlpatterns = [
path('blog/', include('blog.urls', namespace='blog')),
path('shop/', include('shop.urls', namespace='shop')),
]

# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog' # Define the application namespace

urlpatterns = [
path('', views.list, name='list'),
path('<int:post_id>/', views.detail, name='detail'),
]

# shop/urls.py
from django.urls import path
from . import views

app_name = 'shop' # Define the application namespace

urlpatterns = [
path('', views.list, name='list'),
path('<int:product_id>/', views.detail, name='detail'),
]

Using Namespaced URLsā€‹

python
from django.urls import reverse

blog_list_url = reverse('blog:list') # Returns '/blog/'
shop_list_url = reverse('shop:list') # Returns '/shop/'

blog_detail_url = reverse('blog:detail', args=[42]) # Returns '/blog/42/'
shop_detail_url = reverse('shop:detail', args=[100]) # Returns '/shop/100/'

URL Reverse in Templatesā€‹

URL reverse is particularly useful in templates, where Django provides the {% url %} template tag:

html
<!-- Link to the blog list -->
<a href="{% url 'blog:list' %}">All Blog Posts</a>

<!-- Link to a specific blog post -->
<a href="{% url 'blog:detail' post_id=42 %}">View Post #42</a>

<!-- Dynamic URLs with template variables -->
{% for post in posts %}
<a href="{% url 'blog:detail' post_id=post.id %}">{{ post.title }}</a>
{% endfor %}

URL Reverse in Redirectsā€‹

URL reverse is extremely useful for redirects:

python
from django.shortcuts import redirect
from django.urls import reverse

def create_post(request):
# Process form submission...
post = Post.objects.create(title="New Post", content="Content here")

# Redirect to the detail page for the newly created post
return redirect('blog:detail', post_id=post.id)

# Alternative using reverse explicitly
# return HttpResponseRedirect(reverse('blog:detail', args=[post.id]))

Real-World Example: A Blog Applicationā€‹

Let's put everything together in a more comprehensive example of a blog application:

python
# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
path('', views.post_list, name='post_list'),
path('<int:year>/<int:month>/<slug:slug>/', views.post_detail, name='post_detail'),
path('category/<slug:category_slug>/', views.category_posts, name='category_posts'),
path('tag/<slug:tag_slug>/', views.tag_posts, name='tag_posts'),
path('author/<str:username>/', views.author_posts, name='author_posts'),
path('create/', views.post_create, name='post_create'),
path('<int:post_id>/edit/', views.post_edit, name='post_edit'),
]

Using these URL patterns in views:

python
# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from .models import Post, Category
from .forms import PostForm

def post_list(request):
posts = Post.objects.all()
return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, year, month, slug):
post = get_object_or_404(Post,
pub_date__year=year,
pub_date__month=month,
slug=slug)
return render(request, 'blog/post_detail.html', {'post': post})

def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save()
# Redirect to the newly created post
return redirect(
'blog:post_detail',
year=post.pub_date.year,
month=post.pub_date.month,
slug=post.slug
)
else:
form = PostForm()

return render(request, 'blog/post_form.html', {'form': form})

def post_edit(request, post_id):
post = get_object_or_404(Post, id=post_id)

if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save()
# Redirect using reverse with all required parameters
url = reverse('blog:post_detail',
kwargs={
'year': post.pub_date.year,
'month': post.pub_date.month,
'slug': post.slug
})
return redirect(url)
else:
form = PostForm(instance=post)

return render(request, 'blog/post_form.html', {'form': form, 'post': post})

And in the templates:

html
<!-- blog/templates/blog/post_list.html -->
<h1>Blog Posts</h1>

<a href="{% url 'blog:post_create' %}">Create New Post</a>

{% for post in posts %}
<article>
<h2>
<a href="{% url 'blog:post_detail' year=post.pub_date.year month=post.pub_date.month slug=post.slug %}">
{{ post.title }}
</a>
</h2>
<p>Published on {{ post.pub_date }} by
<a href="{% url 'blog:author_posts' username=post.author.username %}">
{{ post.author.username }}
</a>
</p>
<p>
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blog:category_posts' category_slug=category.slug %}">
{{ category.name }}
</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
<div>{{ post.content|truncatewords:30 }}</div>
<a href="{% url 'blog:post_edit' post_id=post.id %}">Edit</a>
</article>
{% empty %}
<p>No posts available.</p>
{% endfor %}

Summaryā€‹

URL reverse is a fundamental Django technique that improves code maintainability and prevents errors due to hardcoded URLs. Let's recap the key points:

  1. URL reverse allows you to reference URLs by name instead of hardcoding paths
  2. Use reverse() in Python code and {% url %} in templates
  3. For class attributes or module-level constants, use reverse_lazy()
  4. URL parameters can be passed using args or kwargs
  5. URL namespaces prevent name collisions across different apps
  6. URL reverse makes redirects more maintainable

By mastering URL reverse, you make your Django code more robust and easier to maintain, especially as your project grows and evolves.

Exercisesā€‹

  1. Create a Django app with several related models (e.g., Author, Book, Review) and implement URL patterns with appropriate naming.
  2. Practice generating URLs with parameters using both args and kwargs approaches.
  3. Implement URL namespaces in a project with multiple apps that have similarly named views.
  4. Create a view that processes a form and redirects to different URLs based on the form's content.

Additional Resourcesā€‹



If you spot any mistakes on this website, please let me know at [email protected]. Iā€™d greatly appreciate your feedback! :)