Loading…

Django 2.0 url() to path() cheatsheet

Django 2.0 introduced a new way to define URLs, which greatly simplifies how parameters are captured.

In earlier versions of Django, you had to use the url() method and pass a regular expressions with named capturing groups to capture URL parameters.

url(r'^posts/(?P<post_id>[0-9]+)/$', post_detail_view)

In Django 2.0, you use the path() method with path converters to capture URL parameters.

path('posts/<int:post_id>/', post_detail_view)

path() always matches the complete path, so path('account/login/') is equivalent to url('^account/login/$').

The part in angle brackets (<int:post_id>) captures a URL parameter that is passed to a view. The part after the colon (post_id) defines the name of the parameter, the part before the colon (int) names a path converter.

Path converters are not only easier to read, they also bring a new feature: path converters can convert parameters to the appropriate type before passing it to the view.

Earlier versions of Django just passed the matched string to the view, meaning you had to write this:

url(r'^posts/(?P<post_id>[0-9]+)/$', post_detail_view)

def post_detail_view(request, post_id):
    post_id = int(post_id)
    ...

Starting with Django 2.0, you would just write this:

path('posts/<int:post_id>')

def post_detail_view(request, post_id):
    ...

Django 2.0 comes with five built-in converters:

  • str
    Matches any non-empty string, excluding the path separator, ‘/’. This is the default if a converter isn’t included in the expression.
    Example: 'best_product_ever!-123.html'
    Returns a string (str).
    Equivalent regular expression: [^/]+

  • int
    Matches zero or any positive integer.
    Example: '1234'
    Returns an integer (int).
    Equivalent regular expression: [0-9]+

  • slug
    Matches any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters.
    Returns a string (str).
    Example: 'building-your-1st-django-site'
    Equivalent regular expression: [-a-zA-Z0-9_]+

  • uuid
    Matches a formatted UUID. To prevent multiple URLs from mapping to the same page, dashes must be included and letters must be lowercase.
    Returns a UUID instance (uuid.UUID).
    Example: '075194d3-6885-417e-a8a8-6c931e272f00'
    Equivalent regular expression: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}

  • path
    Matches any non-empty string, including the path separator, ‘/’. This allows you to match against a complete URL path rather than just a segment of a URL path as with str.
    Example: '/path/to/file'
    Returns a string (str).
    Equivalent regular expression: '.+'

If the pre-defined path converters don’t suit your needs, you can register custom path converters, which can do tricks like converting a URL parameter straight to a model instance. Alternatively, you can still use good ol’ regular expression by using re_path() instead of path().

from django.urls import re_path

re_path(r'posts/(?P<post_id>[0-9]+)/$', post_detail_view)

By the way, the old url() function is still available, so there is no need to change all your URL definitions right now.

I think the new path() method greatly simplifies dealing with URLs. I’ve been working with Django for more than 10 years now and still can’t remember how to define a named regular expression capturing group. Furthermore, manually doing the type conversion violates the DRY-principle.

I hope this little cheat sheet helps you during the transition to Django 2.0. Be sure to check out my follow-up post on how to create powerful custom path converters. Do you have any further question? Don’t hesitate to leave a comment below.


Correction: an earlier version of the image incorrectly listed the last regex as equivalent to slug instead of uuid. Thanks to @m_holtermann for pointing that out!

18 thoughts on “Django 2.0 url() to path() cheatsheet

  1. I often saw just “” as parameter. Does this mean “everything” with the parameter name “token” that I can use in the view then?

  2. Hey,

    In my urls.py, I had the following :

    url(‘^.*’, TemplateView.as_view(template_name = “home.html”)),

    which served some Angular app and Angular routing works from thereon.

    Now I am trying to convert this to use path. I tried following combinations:
    path(‘.*’, TemplateView.as_view(template_name = “home.html”)),
    path(”, TemplateView.as_view(template_name = “home.html”)),
    path(‘*’, TemplateView.as_view(template_name = “home.html”)),

    The second one redirects me to my angular routing. But if I do page refresh on some route of my Angular app, it throws 404 page not found error. It wasnt the case when I was using urls.py. Any suggestions on this?

    Thanks

    1. The path() method is not a good fit for your use-case. My suggestion would be to use re_path(), which is a drop-in replacement for the old url() method.

      from django.urls import re_path
      ...
      re_path(r'^.*', TemplateView.as_view(template_name = "home.html")),

  3. Good day, I have a code that’s written for the older django releases, but I would really like to know how I would write the same code now. url(r’^[0-9]+)$’, views.detail, name=’detail’), I am stuck..

    1. You raise a good point: the new path() method does not support positional arguments, you must provide a name.
      So, assuming the argument to your view is called id (something like def detail(request, id)), your URL definition would look like this:
      path('', views.detail, name=’detail’)
      Alternatively, you could just use re_path() instead of url():
      re_path(r’^[0-9]+)$’, views.detail, name=’detail’)

  4. Hey, how can I put an argument to the path that would be optional? In my case, this is my path:

    path(‘rating/list//’, views.rating_list, name=’rating_list’)

    and I want to put as optional argument the last one, the integer id.

    Thanks you so much!

    1. Hi Carlos,

      I think the commenting form swallowed a

      To make it optional, you have to use a custom converter, something like this:

      class OptionalIntConverter:
          regex = '[0-9]*'
      
          def to_python(self, value):
              if value:
                  return int(value)
              else:
                  return None
      
          def to_url(self, value):
              return str(value) if value is not None else ''
      
      register_converter(OptionalIntConverter, 'optional_int')
      
      
      path(‘rating/list/<optional_int:id>/’, views.rating_list, name=’rating_list’)
      
    1. There is no path converter that matches this pattern.

      The easiest solution is to use re_path:

      re_path(r’^(?P<question>[\w]+)’, views.questions, name = ‘questions’)
      

      (I assume my comment form swallowed something like <question>).

      Alternatively, you will have to write your own path converter:

      from django.urls import converters
      class QuestionConverter(converters.StringConverter):
          regex = '[\w]+
      
      converters.register_converter(QuestionConverter, 'q')
      
      path('<q:question>', views.questions, name = ‘questions’)
      
  5. Hello lad, Im passionately liking the articles on your blog. They are created well, easy to consume and memorize, regardless of English being my third language. All the best.

  6. Your chart at the top helped me alot.
    Thankyou very much for posting it by giving your precious time.

Leave a Reply