#django #django-rest-framework
#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?
Комментарии:
1. Вы отладили то, что
request.META.get('headers').get('Accept')
на самом деле возвращает?==
это довольно строгий тип сравнения по ходу строк.2. Это возвращает true , но кажется, что фактическое согласование содержимого происходит в каком-то совершенно другом месте, и это всегда заканчивается тем, что выбирается первый класс рендеринга.
3. Есть ли в запросе
?format=json
случайно параметр запроса?
Ответ №1:
Ваш модульный тест неправильно устанавливает заголовки. Как описано здесь, при использовании тестового клиента Django следует использовать заголовки в стиле CGI:
response = self.client.get('/api/v1/licenses/', HTTP_ACCEPT='text/uri-list')
Для согласования содержимого используется реальный заголовок HTTP Accept. В вашем коде вы проверяете, что установлен параметр «headers», но это не настоящий заголовок HTTP Accept. Это должно быть:
if request.META.get('HTTP_ACCEPT') == "text/uri-list":
...
Комментарии:
1. Точно! после проверки соответствия средств визуализации я также пришел к выводу, что заголовок вводится неправильно, но я полностью пропустил ту часть, где он неправильно настраивает его в тесте
Ответ №2:
Это блок кода из метода 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
в списке render_classes. Таким образом, сначала будет проверен специальный случай перед более общим случаем во время согласования содержимого. Есть подозрение, что формат запроса также соответствует JSOnRender и он затмевает пользовательский рендерер. Надеюсь, это поможет
Комментарии:
1. Это не должно быть необходимо. Настройка
accepted_renderer
будет работать, но странно, почему она не сопоставляется с использованием согласования по умолчанию.JSONRenderer
Имеет только «application / json» в качестве типа носителя, поэтому он не должен его перехватывать. Также вы должны установитьaccepted_renderer
после второгоif
условия.2. @dirkgroten да, я это исправил.