Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Django›URL Routing — path() and re_path()

URL and Views

URL Routing — path() and re_path()

URL routing is the mechanism Django uses to map incoming HTTP requests to specific view functions or classes based on the requested URL pattern. Understanding these routing methods is critical for building predictable API structures and maintaining clean application navigation. Developers rely on these tools to define how users and machines interact with the application by parsing dynamic variables directly from the browser address bar.

The path() Function and Converters

The path() function is the modern standard for defining URL patterns in Django, offering a human-readable syntax that simplifies common routing tasks. When you define a path, you specify a route string, a view, and an optional name. The power of path() lies in path converters, such as <int:id> or <slug:slug>, which automatically cast incoming data into the appropriate Python types before the request ever hits your view. By using these converters, you shift the burden of validation from your business logic to the routing layer. This separation of concerns ensures that your views remain clean and focused on processing data rather than verifying if a URL segment is a valid integer. Internally, Django compiles these patterns into efficient lookups, making it highly performant for standard routing requirements where fixed segments or basic dynamic parameters are expected.

from django.urls import path
from . import views

# Maps a URL to a view; <int:user_id> ensures only digits are captured.
urlpatterns = [
    path('profile/<int:user_id>/', views.profile_detail, name='user_profile'),
    path('archive/', views.archive_index, name='archive_list'),
]

Leveraging re_path() for Complex Patterns

While path() covers most common scenarios, re_path() allows for the use of regular expressions to define complex, non-standard URL structures. When you need to match patterns that simple converters cannot handle—such as complex date formats, specific alphanumeric constraints that don't fit standard slug definitions, or legacy URL patterns that require exact character count matching—re_path() is the necessary choice. This method gives you total control over the matching process by utilizing the full power of regex groups. It is important to remember that with re_path(), the captured arguments are always passed as strings to the view function. You must therefore handle the conversion, type casting, or validation manually within your view logic. Use this when the constraints of the URL are too specific for standard converters, ensuring your application remains robust even under unusual traffic conditions.

from django.urls import re_path
from . import views

# Uses regex to match a 4-digit year format strictly.
urlpatterns = [
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
]

Order of Operations and Matching Logic

Django processes your urlpatterns list in a linear, top-to-bottom fashion, executing a match as soon as the first pattern satisfies the request path. This sequential processing is vital to understand because it dictates the hierarchy of your routes. If you place a generic pattern before a specific one, the generic one will catch requests meant for the specific route, leading to bugs or unreachability. Always organize your patterns from the most specific to the most general. For example, a route matching '/blog/new/' should always be defined before a route capturing a variable, like '/blog/<slug:slug>/'. If you fail to do this, the system will treat the 'new' literal as a slug parameter rather than its own unique endpoint. This deterministic approach allows developers to explicitly control priority, ensuring that higher-specificity routes have precedence over broader, catch-all URL definitions throughout the configuration.

# Always place 'new' before the dynamic slug to avoid shadowing.
urlpatterns = [
    path('posts/new/', views.create_post),
    path('posts/<slug:slug>/', views.post_detail),
]

Namespacing and Reversing URLs

Hardcoding URLs inside your templates or views is a dangerous practice that makes refactoring difficult and error-prone. Django provides a URL reversing system that allows you to resolve a URL name into an actual address at runtime, provided you define the name parameter in your path(). By using the name argument, you decouple your business logic from the exact URL string. This means you can change the URL in your configuration file, and all links throughout your site will automatically update to reflect the new structure. Furthermore, using application namespaces helps prevent naming collisions in large projects where multiple apps might share common view names. By defining 'app_name' in your urls.py, you can prefix your lookups using the 'app_name:view_name' syntax, guaranteeing that you always reach the intended target without ambiguity regardless of how complex your system becomes.

from django.urls import reverse

# Accessing a URL by name rather than hardcoded string:
# {% url 'user_profile' user_id=123 %}
# Or programmatically in Python:
url = reverse('user_profile', kwargs={'user_id': 123})

Including Additional URL Configurations

As applications grow, keeping all your routing in a single file becomes unmanageable and clutter-heavy. The include() function allows you to delegate parts of the URL routing to other application-specific files, enabling a modular architecture that mirrors your project structure. When you use include(), you effectively tell Django to pause the matching process at the current point and dive into the nested urls.py file. This is essential for scaling, as it keeps the configuration clean and grouped by feature or model type. By breaking your routes into distinct files, you enhance readability and collaboration, as individual developers can work on specific application routes without causing merge conflicts in the primary configuration. Additionally, it makes it easier to test individual application endpoints independently, providing a cleaner separation of your site’s internal topology while maintaining the unified URL space.

from django.urls import path, include

# Delegates traffic starting with 'blog/' to a sub-config.
urlpatterns = [
    path('blog/', include('my_app.urls')),
    path('api/', include('api_app.urls')),
]

Key points

  • The path() function uses converters to automatically cast URL segments into specific data types.
  • Order matters in the urlpatterns list because Django stops at the first pattern that matches.
  • The re_path() function provides full regular expression capabilities for highly complex matching scenarios.
  • Captured values from re_path() are passed as strings, requiring manual validation in the view.
  • Naming routes allows for dynamic URL reversal, preventing broken links during configuration changes.
  • Using namespaces helps distinguish between routes that share the same name across different apps.
  • The include() function modularizes large projects by nesting URL configurations into separate files.
  • Specific routes must always be declared before dynamic routes to avoid accidental parameter shadowing.

Common mistakes

  • Mistake: Forgetting the trailing slash in path strings. Why it's wrong: Django's APPEND_SLASH setting defaults to True, causing an unnecessary redirect if the slash is missing. Fix: Always define URLs with a trailing slash unless specific requirements dictate otherwise.
  • Mistake: Confusing path() converters with regular expressions. Why it's wrong: path() uses simplified syntax like <int:id>, whereas re_path() requires full regex groups like (?P<id>[0-9]+). Fix: Use path() for readability and re_path() only when complex pattern matching is required.
  • Mistake: Overusing re_path() for simple integer or string lookups. Why it's wrong: It makes the codebase harder to read and maintain compared to path(). Fix: Stick to path() unless you need advanced regex features like lookaheads or non-capturing groups.
  • Mistake: Placing a catch-all URL pattern above specific ones. Why it's wrong: Django processes patterns in the order they appear; a generic pattern will shadow more specific ones below it. Fix: Place more specific routes at the top of the urlpatterns list.
  • Mistake: Failing to anchor regular expressions in re_path(). Why it's wrong: Without ^ or $, the pattern might match partial strings unintentionally. Fix: Always use ^ to start and $ to end the pattern to ensure a full match.

Interview questions

What is the fundamental purpose of the path() function in Django's URL configuration?

The path() function is the standard way to define URL patterns in Django. It maps a specific URL string, like 'blog/', to a corresponding view function or class. Its primary purpose is to provide a clean, readable syntax for URL dispatching. By using path converters like <int:id>, Django automatically handles type casting, ensuring that the captured value passed to your view is already an integer, which simplifies view logic and improves code security by enforcing type constraints early in the request cycle.

How does re_path() differ from path() in a Django project?

While path() uses simple, human-readable route converters, re_path() utilizes full Regular Expressions for URL matching. The primary difference is the level of control and complexity; re_path() allows you to match complex patterns, such as identifying a URL that contains a specific string format or conditional structures that standard path converters cannot handle. You should use re_path() when your URL structure cannot be described by simple path() syntax, providing maximum flexibility for advanced routing requirements.

Can you compare and contrast the usage of path() and re_path()?

The choice between path() and re_path() depends on the complexity of your routing needs. path() is preferred for 95% of use cases because it is cleaner, safer, and easier to debug, using syntax like 'posts/<slug:slug>/'. Conversely, re_path() is the 'power user' tool. You should use re_path() only when you require regex features like lookaheads or complex character set matching. In modern Django, it is best practice to default to path() and only reach for re_path() when the standard converters are functionally insufficient for your specific pattern requirements.

What is a URL converter in the context of the path() function, and why should you use them?

A URL converter is a feature in path() that enables you to capture parts of a URL and force them into specific Python types. For instance, using 'path("archive/<int:year>/", ...)' ensures that 'year' arrives in your view as an integer rather than a string. Using these is critical because it offloads validation to Django's URL resolver, reducing the amount of manual type conversion code needed in your view. It also ensures that the view is only called if the URL segment actually matches the expected data type, preventing unnecessary view execution.

How does Django's URL resolver process patterns in the urlpatterns list, and why does order matter?

Django processes the urlpatterns list sequentially from top to bottom. As soon as it finds a match for the requested URL, it stops searching and executes the associated view. Order is critical because more specific patterns must be placed before more general ones. If you have a pattern that catches everything, such as a slug-based URL, and you place it before a specific static URL like 'profile/edit/', the static URL might never be reached because the broader pattern captures it first, causing unexpected 404 errors.

Explain the role of the 'name' argument in path() and re_path(), and why it is a best practice to use it.

The 'name' argument allows you to provide a unique identifier for a specific URL pattern within your Django project. This is crucial because it enables URL reversing using the reverse() function or the {% url %} template tag. By referring to 'profile-edit' instead of hardcoding '/users/edit/1/', your application becomes decoupled from the actual URL structure. If you decide to change your URL configuration later, you only update the urlpatterns file, and every link across your templates and backend code remains functional without requiring any manual updates.

All Django interview questions →

Check yourself

1. Which of the following describes the functional difference between path() and re_path() in Django?

  • A.path() supports named capturing groups, while re_path() only supports positional arguments.
  • B.path() uses simplified route syntax, whereas re_path() allows for full regular expression pattern matching.
  • C.path() is only for static files, while re_path() is for dynamic view routing.
  • D.re_path() is deprecated in newer Django versions and should never be used.
Show answer

B. path() uses simplified route syntax, whereas re_path() allows for full regular expression pattern matching.
Option 2 is correct because path() is designed for simpler, cleaner URL definitions, while re_path() provides the power of Python's re module. Option 1 is incorrect because both support named capturing. Option 3 is incorrect because both route to views. Option 4 is incorrect because re_path() remains a supported utility for complex routing.

2. If your URL pattern is path('profile/<slug:name>/', views.profile), what happens if a user visits '/profile/john-doe_123/'?

  • A.It will raise a 404 error because the slug converter does not support underscores.
  • B.It will raise a 404 error because the slug converter does not support numbers.
  • C.It will match successfully and pass 'john-doe_123' as the name argument.
  • D.It will trigger a 500 server error due to invalid path syntax.
Show answer

C. It will match successfully and pass 'john-doe_123' as the name argument.
Option 3 is correct because the slug converter in Django matches characters including letters, numbers, hyphens, and underscores. Options 1 and 2 are incorrect because slugs explicitly allow these characters. Option 4 is incorrect as the syntax provided is standard for Django.

3. Why is it recommended to order urlpatterns from most specific to least specific?

  • A.Django caches patterns and performs better when sorted by length.
  • B.The first pattern that matches the requested path is the one that is used.
  • C.It prevents the server from throwing a memory error during request parsing.
  • D.It allows Django to ignore trailing slashes automatically.
Show answer

B. The first pattern that matches the requested path is the one that is used.
Option 2 is correct because Django stops searching after the first match. Option 1 is incorrect as order doesn't impact caching. Option 3 is incorrect because order has no effect on memory. Option 4 is incorrect because URL order has nothing to do with slash management.

4. When using re_path(r'^blog/(?P<year>[0-9]{4})/$', views.archive), how does Django pass the 'year' to the view?

  • A.As a keyword argument named 'year'.
  • B.As a positional argument in the order defined.
  • C.As part of the request.GET dictionary.
  • D.It does not pass the year, it must be accessed via request.path.
Show answer

A. As a keyword argument named 'year'.
Option 1 is correct because the ?P<name> syntax explicitly defines a named capture group which is passed to the view as a keyword argument. Option 2 is incorrect because the name syntax forces keyword usage. Options 3 and 4 are incorrect because URL parameters are passed as function arguments.

5. If you want to create a URL pattern that captures a UUID, which is the most efficient and readable method?

  • A.Use re_path(r'^(?P<id>[a-f0-9-]{36})/$', view).
  • B.Use path('<uuid:id>/', view).
  • C.Use path('<str:id>/', view) and validate in the view.
  • D.Use re_path(r'^.+/$', view) and parse the string manually.
Show answer

B. Use path('<uuid:id>/', view).
Option 2 is correct because the built-in uuid converter is the idiomatic and most readable way to handle UUIDs. Option 1 is valid regex but less readable. Option 3 adds unnecessary logic to the view. Option 4 is highly inefficient and prone to errors.

Take the full Django quiz →

← PreviousDjango Admin PanelNext →Function-Based Views (FBV)

Django

30 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app