Skip to content

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-Type header of the incoming request to determine which Parser should process request.data. (e.g., Content-Type: application/json routes to the JSONParser).
  • Rendering (Outgoing Data): DRF examines the Accept header to determine which Renderer should format the response. (e.g., Accept: text/html might route to the BrowsableAPIRenderer or TemplateHTMLRenderer).

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:

python
REST_FRAMEWORK = {
    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation'
}

Or on a per-view basis:

Class-Based Views

python
from rest_framework.negotiation import DefaultContentNegotiation
from rest_framework.views import APIView

class ExampleView(APIView):
    content_negotiation_class = DefaultContentNegotiation

NOTE

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.

python
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.