Returning URLs
NOTE
The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components.
— Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures
As a rule, it is best practice to return absolute URIs from your Web APIs (e.g., https://api.example.com/foobar), rather than returning relative URIs (e.g., /foobar).
The advantages of doing so are:
- Explicit: The URL structure is unambiguous.
- Client-friendly: It leaves less work for your API clients to construct the full endpoint.
- Type safety: There is no ambiguity about the meaning of the string in representations like JSON, which do not have a native URI type.
- Hyperlinked representations: It makes it trivial to mark up HTML representations with hyperlinks.
DRF provides two utility functions to make it simpler to return absolute URIs from your Web API. While you are not required to use them, doing so enables the self-describing browsable API to automatically hyperlink its output for you, making navigating the API much easier.
reverse
DRF provides a reverse utility that mirrors Django's standard reverse, but correctly builds fully-qualified URLs (including protocol, host, and port) by inspecting the incoming HTTP request.
Signature: reverse(viewname, *args, **kwargs)
Has the same behavior as django.urls.reverse, except that it returns a fully qualified URL using the request to determine the host and port.
WARNING
You should always include the request as a keyword argument to the function. Without the request context, DRF cannot construct an absolute URI and will fallback to a relative one.
Usage
from django.utils.timezone import now
from rest_framework.reverse import reverse
from rest_framework.response import Response
from rest_framework.views import APIView
class APIRootView(APIView):
def get(self, request):
year = now().year
data = {
'year-summary-url': reverse('year-summary', args=[year], request=request)
}
return Response(data)from django.utils.timezone import now
from rest_framework.decorators import api_view
from rest_framework.reverse import reverse
from rest_framework.response import Response
@api_view(['GET'])
def api_root(request):
year = now().year
data = {
'year-summary-url': reverse('year-summary', args=[year], request=request)
}
return Response(data)reverse_lazy
Signature: reverse_lazy(viewname, *args, **kwargs)
Has the same behavior as django.urls.reverse_lazy, except that it returns a fully qualified URL, using the request to determine the host and port.
As with the reverse function, you should include the request as a keyword argument to the function.
from rest_framework.reverse import reverse_lazy
api_root = reverse_lazy('api-root', request=request)