Django REST framework, согласование контента - PullRequest
0 голосов
/ 27 марта 2019

Я пытаюсь, чтобы моя конечная точка возвращала 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 ]

2 голосов
/ 27 марта 2019

Ваш юнит-тест неправильно настраивает заголовки. Как описано здесь , вы должны использовать заголовки в стиле CGI при использовании тестового клиента Django:

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 голос
/ 27 марта 2019

Это блок кода из метода 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)

Еще одна вещь, которую вы, вероятно, должны сделать, это поместить свой пользовательский класс рендерера до JSONRenderer в список renderer_classes. Таким образом, он сначала проверяет особый случай перед более общим случаем во время согласования контента. Подозревается, что формат запроса также соответствует JSOnRender, и это затмевает пользовательский рендер. Надеюсь, это поможет

...