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.