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
The name of the URL pattern to reverse
Positional arguments for the URL pattern
Keyword arguments for the URL pattern
If provided, returns a fully qualified URL. Also enables versioning support
Format suffix to append to the URL (e.g., 'json')
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,
})
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'
# 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()
| Feature | Django reverse() | DRF reverse() |
|---|
| Basic URL reversal | Yes | Yes |
| Fully qualified URLs | No | Yes (with request) |
| Format suffixes | No | Yes (with format) |
| Versioning support | No | Yes (automatic) |
| Query parameter preservation | No | Yes (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