DRF: Content Negotiation

DefaultContentNegotiation

content_negotiation_classをカスタマイズするには、以下のようにします。

1
2
3
4
5
6
from rest_framework.negotiation import DefaultContentNegotiation

class MyContentNegotiation(DefaultContentNegotiation):
    def select_renderer(self, request, renderers, format_suffix=None):
        # ここに処理を書きます。
        pass

上記の例では、DefaultContentNegotiationを継承しています。

select_renderer()メソッドには、レンダラーを選択するための処理を書きます。 このメソッドは、リクエストオブジェクト、レンダラーのリスト、およびフォーマットサフィックスを引数として受け取ります。

ソース: Bing との会話 2023/5/12

ViewSetでの設定

ViewSetごとにcontent_negotiation_classを設定することができます。

以下のように、ViewSetクラスのcontent_negotiation_class属性にカスタムクラスを指定します。

1
2
3
4
5
6
from rest_framework.viewsets import ModelViewSet

class MyViewSet(ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer
    content_negotiation_class = MyContentNegotiation

上記の例では、MyContentNegotiationというカスタムクラスを使用しています。このクラスは、前回の回答で紹介したものです。

バイナリファイルの処理

バイナリレンダラ:

1
2
3
4
5
from rest_framework_csv import renderers

class BinaryRenderer(renderers.BaseRenderer):
    def render(self, data, media_type=None, renderer_context=None):
        return data

ネゴシエータ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from rest_framework.negotiation import DefaultContentNegotiation
from mimetypes import guess_type


class AttachmentContentNegotiation(DefaultContentNegotiation):
    def select_renderer(self, request, renderers, format_suffix=None):
        try:
            res = super().select_renderer(request, renderers, format_suffix=format_suffix)
            return res
        except Exception as e:
            mt = format_suffix and guess_type(f"a.{format_suffix}")[0]
            logger.info(f"{e}:BinaryRenderer for {format_suffix}")
            return (base_renderers.BinaryRenderer(), mt)
1
2
3
4
5
6
7
8
9
class AttachmentViewSet(viewsets.BaseModelViewSet):
    permission_classes = [
        permissions.AttachmentPermission,
    ]
    filterset_class = filters.AttachmentFilter
    serializer_class = serializers.AttachmentSerializer
    queryset = models.Attachment.objects.order_by("id")
    pagination_class = paginations.Pagination
    content_negotiation_class = AttachmentContentNegotiation