Skip to main content
DRF provides utilities for reversing URLs that integrate with versioning and format suffixes.

reverse

Reverse a URL, returning a fully qualified URL if a request is provided.
from rest_framework.reverse import reverse

url = reverse('user-detail', args=[user.id], request=request)
# Returns: 'http://example.com/api/users/123/'

Signature

reverse(
    viewname,
    args=None,
    kwargs=None,
    request=None,
    format=None,
    **extra
)

Parameters

viewname
str
required
The name of the URL pattern to reverse
args
list
Positional arguments for the URL pattern
kwargs
dict
Keyword arguments for the URL pattern
request
Request
If provided, returns a fully qualified URL. Also enables versioning support
format
str
Format suffix to append to the URL (e.g., 'json')
**extra
dict
Additional keyword arguments passed to Django’s reverse()

Returns

A URL string. If request is provided, returns a fully qualified URL including the scheme and hostname.

reverse_lazy

Lazy version of reverse() that delays evaluation until the URL is actually needed.
from rest_framework.reverse import reverse_lazy

url = reverse_lazy('user-detail', args=[user.id])
Useful for:
  • Class-level URL definitions
  • URLs needed at import time
  • URLs in model definitions

Usage Examples

Basic Usage

from rest_framework.reverse import reverse
from rest_framework.views import APIView
from rest_framework.response import Response

class UserDetail(APIView):
    def get(self, request, pk):
        user = User.objects.get(pk=pk)
        # Generate URL for related resource
        posts_url = reverse('user-posts', args=[pk], request=request)
        return Response({
            'id': user.id,
            'username': user.username,
            'posts_url': posts_url,
        })

With Format Suffixes

from rest_framework.reverse import reverse

# Generate URL with JSON format suffix
url = reverse('user-detail', args=[123], request=request, format='json')
# Returns: 'http://example.com/api/users/123.json'

With Versioning

from rest_framework.reverse import reverse

# When using versioning, reverse() automatically includes the version
url = reverse('user-detail', args=[123], request=request)
# With URLPathVersioning: 'http://example.com/v1/users/123/'
# With QueryParameterVersioning: 'http://example.com/users/123/?version=v1'

In Serializers

from rest_framework import serializers
from rest_framework.reverse import reverse

class UserSerializer(serializers.ModelSerializer):
    url = serializers.SerializerMethodField()
    posts_url = serializers.SerializerMethodField()
    
    class Meta:
        model = User
        fields = ['id', 'username', 'url', 'posts_url']
    
    def get_url(self, obj):
        request = self.context.get('request')
        return reverse('user-detail', args=[obj.id], request=request)
    
    def get_posts_url(self, obj):
        request = self.context.get('request')
        return reverse('user-posts', args=[obj.id], request=request)

Without Request (Relative URLs)

from rest_framework.reverse import reverse

# Without request, returns relative URL
url = reverse('user-detail', args=[123])
# Returns: '/api/users/123/'

With Current App

from rest_framework.reverse import reverse

# Use current_app parameter for namespaced URLs
url = reverse(
    'user-detail',
    args=[123],
    request=request,
    current_app='api_v2'
)

Integration with Versioning

The reverse() function automatically integrates with versioning schemes:

URLPathVersioning

# With URLPathVersioning
request.version = 'v1'
url = reverse('user-detail', args=[123], request=request)
# Returns: 'http://example.com/v1/users/123/'

NamespaceVersioning

# With NamespaceVersioning
request.version = 'v2'
url = reverse('user-detail', args=[123], request=request)
# Internally reverses 'v2:user-detail'
# Returns: 'http://example.com/v2/users/123/'

QueryParameterVersioning

# With QueryParameterVersioning
request.version = 'v1'
url = reverse('user-detail', args=[123], request=request)
# Returns: 'http://example.com/users/123/?version=v1'

AcceptHeaderVersioning

# With AcceptHeaderVersioning (no URL modification)
request.version = '1.0'
url = reverse('user-detail', args=[123], request=request)
# Returns: 'http://example.com/users/123/'
# Version is in Accept header, not URL

preserve_builtin_query_params

Internal function that preserves built-in query parameters when generating URLs.
from rest_framework.reverse import preserve_builtin_query_params

url = preserve_builtin_query_params(url, request)
Automatically preserves:
  • Format override parameter (URL_FORMAT_OVERRIDE setting)

Comparison with Django’s reverse()

FeatureDjango reverse()DRF reverse()
Basic URL reversalYesYes
Fully qualified URLsNoYes (with request)
Format suffixesNoYes (with format)
Versioning supportNoYes (automatic)
Query parameter preservationNoYes (automatic)

Best Practices

Always Pass Request in Serializers

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username']

# In view, always pass request in context
serializer = UserSerializer(user, context={'request': request})

Use HyperlinkedModelSerializer

For automatic URL generation:
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username', 'posts']

# Automatically generates URLs using reverse()

Handle Missing Request Gracefully

def get_user_url(user, request=None):
    try:
        return reverse('user-detail', args=[user.id], request=request)
    except NoReverseMatch:
        return None

Build docs developers (and LLMs) love