Dec 02, 2025
Aris S. & Ariffud M.
7min Read
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:
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:
For better organization, especially in larger projects, you can define URL patterns at both the project and application level.
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/.
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:
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.
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.
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:
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.
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.
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.
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.
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:
| Feature | Path converters | Regular expressions (regex) |
| Readability | High. 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 safety | High. 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. |
| Flexibility | Good 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 prone | Low. 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.
Following best practices for your URL configurations will keep your Django project organized, scalable, and easy to debug.
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.
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.
Similarly, give your URL patterns descriptive names:
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.
<a href="/blog/post/{{ post.id }}/"><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.

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.