Content Negotiation
Content negotiation is the process of selecting the most appropriate data format to return to a client based on their preferences. In Django REST Framework, content negotiation acts as the routing mechanism that decides exactly which Parser and which Renderer should be used for a given request.
How it works
DRF relies on standard HTTP headers to perform this routing:
- Parsing (Incoming Data): DRF examines the
Content-Typeheader of the incoming request to determine which Parser should processrequest.data. (e.g.,Content-Type: application/jsonroutes to theJSONParser). - Rendering (Outgoing Data): DRF examines the
Acceptheader to determine which Renderer should format the response. (e.g.,Accept: text/htmlmight route to theBrowsableAPIRendererorTemplateHTMLRenderer).
Default Behavior
By default, DRF uses DefaultContentNegotiation. This class respects the client's Accept headers but will gracefully fall back to the first renderer in your renderer_classes list if the client's preferences cannot be strictly met.
You can explicitly set or override this globally in your settings.py:
REST_FRAMEWORK = {
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation'
}Or on a per-view basis:
Class-Based Views
from rest_framework.negotiation import DefaultContentNegotiation
from rest_framework.views import APIView
class ExampleView(APIView):
content_negotiation_class = DefaultContentNegotiationNOTE
Function-based views (@api_view) always use the globally configured content negotiation class. DRF does not provide a decorator to override this on a per-view basis for FBVs.
Custom Content Negotiation
While DefaultContentNegotiation handles 99% of use cases, you might need a custom scheme. For example, you might want to completely ignore the client's Accept headers and always force a specific response format based on the URL or the requesting user's profile.
To implement a custom scheme, subclass BaseContentNegotiation and override the .select_parser() and .select_renderer() methods.
from rest_framework.negotiation import BaseContentNegotiation
class IgnoreClientContentNegotiation(BaseContentNegotiation):
"""
A custom negotiator that ignores client headers and simply
forces the first available parser and renderer defined on the view.
"""
def select_parser(self, request, parsers):
# `parsers` is the list of Parser instances configured on the view
return parsers[0]
def select_renderer(self, request, renderers, format_suffix):
# `renderers` is the list of Renderer instances configured on the view
# `format_suffix` is an optional URL suffix (like '.json' or '.xml')
# Return a tuple of (Renderer instance, Media type string)
return (renderers[0], renderers[0].media_type)By returning the renderer and media type directly, you explicitly dictate how the Response will be formatted, bypassing standard HTTP negotiation rules entirely.
