DateConverter for URL resolving with Django

· Gam Guerrero's blog

How to ensure you get ISO8601 date strings in the URL path

Table of Contents

The problem #

A few days ago, I wanted to add a new URL path to a personal Django project that I use for my archives. I wanted to scope this URL path by date, so I decided to use the sample snippet shown in Django's docs.

 1from django.urls import path
 2
 3from . import views
 4
 5urlpatterns = [
 6    path("articles/2003/", views.special_case_2003),
 7    path("articles/<int:year>/", views.year_archive),
 8    path("articles/<int:year>/<int:month>/", views.month_archive),
 9    path("articles/<int:year>/<int:month>/<slug:slug>/", views.article_detail),
10]

More specifically, I'm referring to the following line:

1path("articles/<int:year>/<int:month>/<slug:slug>/", views.article_detail)

Which can be paired with a view like this:

1def article_detail(request, year: int, month: int, day: int, slug: str):
2    article = get_object_or_404(Article, published_at__date=datetime.date(year, month, day))

Naively, I thought that would be it. The URL resolved successfully, and pairing those date components with an ORM query yielded the expected results. However, once I inspected the reverse URL values, I noticed that month and day weren’t being zero-padded, which caused invalid ISO8601 date strings.

The tools #

Django supports something called path converters, that can be used along with path, and they even have an example for a 4 year digit:

 1class FourDigitYearConverter:
 2    regex = "[0-9]{4}"
 3
 4    def to_python(self, value):
 5        return int(value)
 6
 7    def to_url(self, value):
 8        return "%04d" % value
 9
10
11register_converter(converters.FourDigitYearConverter, "yyyy")
12
13urlpatterns = [
14    path("articles/2003/", views.special_case_2003),
15    path("articles/<yyyy:year>/", views.year_archive),
16    ...,
17]

That worked to some extent, and at first I considered making a YearConverter, MonthConverter and DayConverter, however after thinking about it for about 30 seconds I noticed I could use a single converter for it.

The solution #

Hence I wrote this tiny DateConverter that handles ISO8601 date strings for URL paths:

 1# converters.py
 2import datetime
 3
 4class DateConverter:
 5    regex = r"\d{4}-\d{2}-\d{2}"
 6
 7    def to_python(self, value: str) -> datetime.date:
 8        return datetime.date.fromisoformat(value)
 9
10    def to_url(self, value: datetime.date) -> str:
11        return value.isoformat()
 1# urls.py
 2from django.urls import register_converter
 3
 4register_converter(DateConverter, "date")
 5
 6urlpatterns = [
 7    path(
 8        "article/<date:published_at>/<slug:slug>/",
 9        views.article_detail,
10    ),
11]
1# views.py
2def article_detail(request, published_at: datetime.date, slug: str):
3    article = get_object_or_404(Article, published_at=published_at)

That’s it! With this converter, we can now have URLs like this one:

article/2025-08-30/slug

Now I'll check if I can commit this to Django's codebase.

Update #

Well, I just went ahead and created a ticket along with a pull request to Django's code base, let's hope this gets merged something in the future.

Another Update #

Unfortunatelly my patch was rejected with a respectful explanation, and I agree since this change was really small.

last updated: