SyntaxStudy
Sign Up
Django Custom Path Converters and URL Best Practices
Django Beginner 1 min read

Custom Path Converters and URL Best Practices

Django's built-in path converters (str, int, slug, uuid, path) cover most use cases, but you can also create custom converters for specialised URL patterns. A custom converter is a class with a regex attribute defining the matching pattern, a to_python() method that converts the URL string to a Python object, and a to_url() method that converts a Python object back to a URL string. You register converters with register_converter(). Choosing good URL patterns is important for both usability and SEO. URLs should be human-readable, use hyphens rather than underscores as word separators, avoid exposing internal database IDs where possible (use slugs instead), and follow REST-like conventions for resource-based applications. Django's slugify() function and SlugField model field make it easy to generate URL-safe identifiers from titles. Always name your URL patterns and use reverse() or {% url %} to generate URLs rather than hardcoding strings. This practice, sometimes called "Don't Repeat Yourself" (DRY) for URLs, ensures that changing a URL pattern in one place automatically propagates throughout the application. It also makes testing easier since you can refer to views by name rather than by URL string.
Example
# blog/converters.py — custom path converter
class FourDigitYearConverter:
    regex = '[0-9]{4}'

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return f'{value:04d}'


# blog/urls.py — register and use custom converter
from django.urls import path, register_converter
from . import converters, views

register_converter(converters.FourDigitYearConverter, 'yyyy')

urlpatterns = [
    path('archive/<yyyy:year>/', views.PostYearArchiveView.as_view(), name='post-year'),
]

# Slug-based URLs instead of IDs
# blog/models.py
from django.db import models
from django.utils.text import slugify


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug  = models.SlugField(max_length=200, unique=True, blank=True)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        from django.urls import reverse
        return reverse('blog:post-detail', kwargs={'slug': self.slug})


# blog/urls.py with slug
urlpatterns = [
    path('<slug:slug>/', views.PostDetailView.as_view(), name='post-detail'),
]