In Django, using urlpatterns, how do I match a url-encoded slash character as part of a parameter?

Viewed 139

In my Django app I have these urlpatterns:

urlpatterns = [
    url(r'^$', schema_view, name='swagger'),
    url(r'^(?P<page_title>.+)/(?P<rev_id>[0-9]+)/$',
        WhoColorApiView.as_view(), name='wc_page_title_rev_id'),
    url(r'^(?P<page_title>.+)/$',
        WhoColorApiView.as_view(), name='wc_page_title'),
]

The second entry will match a path like /en/whocolor/v1.0.0-beta/my_book_title/1108730659/?origin=* and the third will match a path like /en/whocolor/v1.0.0-beta/my_book_title/?origin=*.

The idea is that it's matching a page_title parameter (a Wikipedia article title), and an optional integer rev_id (revision id) parameter.

However, it does not work as intended for an article titled "Post-9/11", with path /en/whocolor/v1.0.0-beta/Post-9%2f11/?origin=*. I want this not to match the second pattern (which gets matched as page_title "Post-9" and rev_id 11), and instead match the third pattern.

I understand why the second pattern should match if the path is /en/whocolor/v1.0.0-beta/Post-9/11/?origin=*, but when the title is url-encoded as "Post-9%2f11" it still matches the second pattern instead of continuing on to the third pattern.

How can I make Django treat the url-encoded slash as part of the parameter instead of a path separator?

1 Answers

In Django there's something called Path Converters.

One of the existing path converters deals with things with slashes

path - Matches any non-empty string, including the path separator, '/'. This allows you to match against a complete URL path rather than a segment of a URL path as with str.

path('<path:page_title>/',
         views.post_detail,
         name='post_detail'),

When one uses path one doesn't need to escape anything, and it'll work. There are various cases similar to OP's where Path was used as a solution, such as:


As Ivan Starostin mentions in a comment, OP should actually use the path converter slug.

According to the docs

A slug is a short label for something, containing only letters, numbers, underscores or hyphens.

For example, building-your-1st-django-site.

To do so, it's advisable that OP uses, in the model, a field of type SlugField.

slug = models.SlugField(max_length=250)

Then in the urls.py

path('<slug:post>/',
         views.post_detail,
         name='post_detail'),
Related