Django URL patterns – how they work and how to use them

Django URL patterns – how they work and how to use them

Django URL patterns define how requests are routed to views in a Django application. They act as a table of contents for your website, matching the URL a visitor enters in their browser to the specific content you want to display.

To effectively use Django URL patterns, it’s important to understand key principles:

  • URL pattern mechanics. Django uses URL patterns for routing, with configurations managed at both the project and app levels.
  • Creating and using URL patterns. This involves mapping URLs to views, using dynamic parameters for variable data, and applying named patterns for improved code maintainability.
  • Path converters vs. regex. While regular expressions offer flexibility, path converters are the modern, cleaner standard for capturing URL parameters.
  • URL pattern best practices. Following best practices ensures your URL configurations remain clean, efficient, and scalable as your project grows.

How do URL patterns work in Django?

In Django, URL patterns work by matching a user’s requested URL against a list of predefined paths in your project’s configuration. When a request comes in, Django follows a specific algorithm to find the correct view to execute.

Here’s the process, simplified:

  1. Django identifies the root URL configuration (URLconf) module to use, which you usually define in your project’s settings.py file.
  2. It loads that module and looks for a variable named urlpatterns. This variable holds a list of URL patterns for your site.
  3. Django goes through each pattern in the urlpatterns list in order. It stops at the first pattern that matches the requested URL.
  4. Once Django finds a match, it calls the associated view function, passing the request details and any parameters captured from the URL. If no pattern matches, Django returns an error page.

Project-level vs. app-level URLs

For better organization, especially in larger projects, you can define URL patterns at both the project and application level.

  • Project-level URLs are defined in the urls.py file located in your main project directory (the same folder as settings.py). These patterns handle site-wide routing, such as directing requests to different apps – like a blog or a shop – or managing the admin interface.
  • App-level URLs are specific to a single application. You create a separate urls.py file inside your app’s directory to manage URLs related only to that app, like individual blog posts or product pages.

This separation makes your project more modular and easier to maintain. To connect them, you use the include() function in your project-level urls.py to point to the app-level URL configurations.

For example, here’s how you would set up a project named myproject with a blog app. In your project-level myproject/urls.py file, you would include the blog’s URLs:

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')), # Directs any URL starting with 'blog/' to the blog app
]

Then, in your app-level blog/urls.py file, you define the patterns specific to the blog:

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

urlpatterns = [
    path('', views.blog_index, name='blog-index'),
    path('post/<int:post_id>/', views.post_detail, name='post-detail'),
]

With this setup, the blog_index view handles requests to yourdomain.tld/blog/, while the post_detail view handles requests to yourdomain.tld/blog/post/5/.

How do I create a URL pattern in Django?

You create a URL pattern in Django by defining it within the urlpatterns list in a urls.py file using the path() function. This function maps a URL route to a specific view function that handles the logic for that page.

First, make sure to import the path function and your Django app’s views at the top of your urls.py file:

from django.urls import path
from . import views

Next, define your patterns inside the urlpatterns list. The path() function takes three main arguments:

  • route. A string that contains the URL pattern to match. For example, ‘contact/’ or ‘articles/<int:year>/’.
  • view. The view function that Django should call when the route is matched.
  • name (optional but highly recommended). A unique name for this URL pattern, which lets you refer to it easily from other parts of your Django project.

Here is a basic example of mapping two static URLs to their respective views:

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

urlpatterns = [
   path('archive/', views.archive, name='archive-page'),
   path('disclaimer/', views.disclaimer, name='disclaimer-page'),
]

In this example, visiting yourdomain.tld/archive/ will trigger the archive function in views.py, while accessing yourdomain.tld/disclaimer/ will trigger the disclaimer function.

How to use Django URL patterns

Once you’ve defined your basic URLs, you can start implementing more powerful features like dynamic patterns, URL reversing, and clear routing to your views.

Dynamic URL patterns and parameters

Most websites need more than just static pages. For example, a blog needs a unique URL for each post. Instead of creating a separate URL pattern for every single post, you can create a dynamic URL pattern that captures parameters from the URL.

You do this using path converters, which are placeholders in your URL route that capture a piece of the URL and pass it to your view as an argument. They are specified using angle brackets <>.

Here are the most common built-in path converters:

  • <int:name>. Matches any positive integer and passes it to the view as an integer. Ideal for things like post IDs.
    • Example: path(‘post/<int:post_id>/’, views.post_detail) matches /post/123/.
  • <str:name>. Matches any non-empty string, excluding the / character. This is the default converter if you don’t specify one.
    • Example: path(‘user/<str:username>/’, views.user_profile) matches /user/alex/.
  • <slug:name>. Matches a “slug” – a string containing only letters, numbers, hyphens, or underscores. Perfect for SEO-friendly URLs.
    • Example: path(‘post/<slug:post_slug>/’, views.post_detail) matches /post/my-first-django-post/.
  • <uuid:name>. Matches a universally unique identifier (UUID). Useful for things like order confirmation numbers.
    • Example: path(‘order/<uuid:order_id>/’, views.order_detail) matches /order/123e4567-e89b-12d3-a456-426614174000/.

Here’s an example showing how to capture a post ID:

# Using a path converter (recommended)
path('blog/post/<int:post_id>/', views.post_detail, name='post-detail')

This pattern matches URLs like blog/post/101/ or blog/post/32/. The post_detail view receives the captured value (101 or 32) as an argument.

For comparison, here is how the same pattern would look using a regular expression with re_path():

# Using a regular expression (older method)
from django.urls import re_path

re_path(r'^blog/post/(?P<post_id>[0-9]+)/$', views.post_detail, name='post-detail')

As you can see, the path converter is much more concise and readable.

Reversing URLs in Django

URL reversing is Django’s mechanism for generating URLs from their name and any required parameters. This approach is extremely useful because it prevents you from hardcoding URLs in your templates and views.

In Django templates, you use the {% url %} template tag for URL reversal. This tag takes the name of the URL pattern and any arguments it needs to build the final URL.

For instance, if you have this URL pattern:

# myapp/urls.py
path('post/<int:post_id>/', views.post_detail, name='post-detail')

You can create a link to a specific post in your template like this:

<a href="{% url 'post-detail' post.id %}">Read More</a>

Django will replace {% url ‘post-detail’ post.id %} with the correct URL, such as /post/123/. If you ever decide to change the URL structure to /article/123/, you only need to update the path() in urls.py.

All your templates will automatically generate the new URL without any additional changes.

URL routing with views

The main purpose of a URL pattern is to connect a URL to a view function in your views.py file. When a dynamic URL captures a parameter, Django passes that parameter as an argument to the view function.

Let’s connect the pieces. Here is the URL pattern from your urls.py file:

# myapp/urls.py
urlpatterns = [
    path('archive/', views.archive, name='archive-page'),
]

And here is the corresponding view function in views.py:

# myapp/views.py
from django.shortcuts import render

def archive(request):
    # This function could fetch data from a database, for example.
    # For simplicity, we'll just render a template.
    return render(request, 'archive.html')

When a user visits /archive/, Django finds the matching pattern, calls the views.archive function, and passes it the request object. The function then executes and returns an HTTP response, in this case, by rendering the archive.html template.

Naming URL patterns in Django

The previous examples show that naming your URL patterns with the name argument in the path() function is a crucial best practice for URL reversing.

path('product/<slug:category>/<int:id>/', views.product_detail, name='product-detail')

When choosing names, it’s important to be descriptive and avoid conflicts with other apps. A common convention is to prefix the name with the app’s name, like product-detail instead of just detail.

This prevents name collisions if you have another app with a detail view.

Regular expressions vs. path converters

Before Django introduced path converters, it relied on regular expressions (regex) to define complex URL patterns using the re_path() function.

Path converters are now the recommended approach for most use cases because they are much cleaner and easier to read.

Let’s take a look at the comparison table below:

FeaturePath convertersRegular expressions (regex)
ReadabilityHigh. The syntax is clear and concise (for example, <int:id>).Low. The syntax is complex and can be hard to read (for example, (?P<id>[0-9]+)).
Type safetyHigh. They automatically convert the matched string to the correct type (for example, an integer).Low. Regex always captures strings; you must manually convert types in the view.
FlexibilityGood for common use cases. You can also create custom converters for complex needs.Very high. Can match almost any pattern imaginable, but with added complexity.
Error proneLow. The simple syntax reduces the chance of making mistakes.High. It’s easy to write a regex pattern that is subtly incorrect.

While path converters cover most needs, regex is still useful for highly specific or complex patterns that built-in converters don’t support. For example, if you needed to match a URL that contains a two-digit country code followed by a specific number format.

However, for everyday tasks like capturing IDs, slugs, or usernames, always prefer path converters.

What are some of the best practices for Django URL patterns?

Following best practices for your URL configurations will keep your Django project organized, scalable, and easy to debug.

Use trailing slashes and avoid URL overlap

By default, Django’s APPEND_SLASH setting automatically redirects URLs without a trailing slash to the same URL with one.

To avoid unnecessary redirects and to maintain consistency, it’s best practice to always add a trailing slash to your URL patterns. If a URL requires a final slash but you request it without one, Django won’t match it.

Also, structure your URL patterns carefully. Django processes patterns in order in the urlpatterns list. More specific patterns should always come before more general ones.

For example, path(‘posts/new/’, …) should be placed before path(‘posts/<slug:post_slug/’, …) so that a request to /posts/new/ doesn’t get incorrectly matched as a post with the slug new.

Follow clear and descriptive naming conventions

Make URLs readable so they give a clear clue about the content of the page. A descriptive URL helps with SEO and makes the site easier to navigate.

This applies to both the URL itself and the name you give the pattern.

  • Bad: /blog/p/<int:id>/ (uses an unclear abbreviation p)
  • Good: /blog/post/<int:id>/ (clear and descriptive)

Similarly, give your URL patterns descriptive names:

  • Bad: name=’p_detail’
  • Good: name=’post-detail’

Avoid hardcoding URLs

Never hardcode URLs directly in your templates or Python code. Always use the {% url %} template tag or the reverse() function to generate URLs from their names.

  • Bad (hardcoded): <a href="/blog/post/{{ post.id }}/">
  • Good (dynamic): <a href="{% url 'post-detail' post.id %}">

This practice is essential for scalability. If you change a URL, you only have to update it in one place – your urls.py file – instead of searching through dozens of files for every old instance.

For large projects, group related URLs by using the include() function. For example, keep all blog-related URLs in a blog/urls.py file, all shop-related URLs in a shop/urls.py file, and so on.

This keeps your main project urls.py file clean and makes it easier to manage each app’s routes independently.

What’s next? Learn more about Django static files

Mastering URL patterns is a fundamental step in building a Django application. They act as the central router, connecting your users’ requests to the correct logic in your views.

By following best practices for naming, structure, and dynamic routing, you can create a scalable and maintainable web application.

Now that you know how to handle your site’s URLs, the next logical step is to manage static assets that make your site look and function correctly, such as CSS, JavaScript, and images.

To continue your journey, learn how to configure static files in Django. This helps you optimize page load times, keep your site’s design consistent, and deliver a better user experience.

All of the tutorial content on this website is subject to Hostinger's rigorous editorial standards and values.

Author
The author

Aris Sentika

Aris is a Content Writer specializing in Linux and WordPress development. He has a passion for networking, front-end web development, and server administration. By combining his IT and writing experience, Aris creates content that helps people easily understand complex technical topics to start their online journey. Follow him on LinkedIn.

Author
The Co-author

Ariffud Muhammad

Ariffud is a Technical Content Writer with an educational background in Informatics. He has extensive expertise in Linux and VPS, authoring over 200 articles on server management and web development. Follow him on LinkedIn.