Django REST框架,内容协商

时间:2019-03-27 13:51:31

标签: django django-rest-framework

我正在尝试让我的端点在被要求返回一个uri-list时返回一个json字符串作为默认值。我在单元测试中对此进行了测试,如下所示:

[...]
headers = {'Accept': 'text/uri-list'}
response = self.client.get('/api/v1/licenses/', headers=headers)
[...]

我这样写了URIListRenderer

from rest_framework import renderers


class URIListRenderer(renderers.BaseRenderer):
media_type = 'text/uri-list'

def render(self, data, media_type='None', renderer_context=None):
    return "\n".join(data).encode()

接下来,我尝试在我的视图中使用我的渲染器渲染响应:

class RestLicenses(APIView):    
    """
    List all licenses, or create a new license
    """
    permission_classes = (IsAuthenticated,)
    parser_classes = (MultiPartParser,)
    renderer_classes = (JSONRenderer, URIListRenderer,)

    def get(self, request, format=None,):
        models = LicenseModel.objects.all()
        if len(models) == 0 :
            return Response('[]',status=204)
        if request.META.get('headers') is not None :
            if request.META.get('headers').get('Accept') == 'text/uri-list' :
                result = [];
                for m in models :
                    result.append(reverse('downloadLicense', args=[m.pk], request=request))
                return Response(result, status=200)

        serializer = LicenseJSONSerializer(request, models, many=True)
        serializer.is_valid()
        return HttpResponse(JSONRenderer().render(serializer.data), content_type='application/json', status=200)

但是,除了列表中的第一个渲染器外,似乎很难选择其他任何渲染器。如何使其选择我的URIListRenderer而不是json呢?

2 个答案:

答案 0 :(得分:2)

您的单元测试未正确设置标题。如here所述,在使用Django测试客户端时,您应该使用CGI样式标头:

response = self.client.get('/api/v1/licenses/', HTTP_ACCEPT='text/uri-list')

内容协商使用真实的HTTP Accept标头。在您的代码中,您检查是否设置了“标头”,但这不是真正的HTTP Accept标头。应该是:

if request.META.get('HTTP_ACCEPT') == "text/uri-list":
    ... 

答案 1 :(得分:1)

这是方法finalize_response中的代码块:

if isinstance(response, Response):
    if not getattr(request, 'accepted_renderer', None):
        neg = self.perform_content_negotiation(request, force=True)
        request.accepted_renderer, request.accepted_media_type = neg

    response.accepted_renderer = request.accepted_renderer
    response.accepted_media_type = request.accepted_media_type
    response.renderer_context = self.get_renderer_context()

如您所见,在执行内容协商之前,它会检查视图是否已经设置了所需的渲染器,如果不是,则尝试自行进行协商。 因此,您可以在get方法中执行此操作:

if request.META.get('headers') is not None :
        if request.META.get('headers').get('Accept') == 'text/uri-list' :
            request.accepted_renderer = URIListRenderer
            result = [];
            for m in models :
                result.append(reverse('downloadLicense', args=[m.pk], request=request))
            return Response(result, status=200)

您可能应该做的另一件事是将自定义渲染器类放在renderer_classes列表中JSONRenderer之前。这样,它将在内容协商期间先检查特殊情况,然后再检查一般情况。怀疑请求格式也与JSOnRender匹配,并且使自定义渲染器不正确。希望这会有所帮助