我有一个REST视图集,如果请求带有特定标志,我希望能够执行重定向到非REST视图。我想要:
我的视图集GET
,在请求中设置flag
时重定向到另一个视图,否则返回JSON响应。
如果客户端设置了Accept
HTTP,因此可以使用JSON或HTML,我希望如果未设置flag
,则返回的格式为JSON; flag
时为HTML }已设置。 (也就是说,默认格式应根据是否设置flag
而有所不同。)
唯一有效的响应格式是设置标志时的HTML,以及未设置标志时的JSON。
这就是我所拥有的:
class FooViewSet(mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
queryset = SomeModel.objects
serializer_class = SomeModelSerializer
lookup_field = "pk"
renderer_classes = (renderers.JSONRenderer, )
def retrieve(self, request, *args, **kwargs):
# The original code validates the value of `flag` but
# that's not relevant to the issue here.
flag = request.GET.get('flag', None)
if flag:
if request.accepted_renderer.media_type != "text/html":
raise NotAcceptable
return HttpResponseRedirect(SomeModel.objects.get(pk=kwargs["pk"])
.get_absolute_url())
if request.accepted_renderer.media_type != "application/json":
raise NotAcceptable
return super(FooViewSet, self).retrieve(request, *args, **kwargs)
上面代码的问题在于,已接受的渲染器从未将media_type
设置为text/html
,因为我没有接受text/html
的渲染器。我不想将renderers.StaticHTMLRenderer
添加到renderer_classes
,因为我实际上并不想使用该渲染器,而是直接返回HttpResponseRedirect
。
答案 0 :(得分:0)
您需要进行设置,以便实现内容协商的DRF APIView
(通过viewsets.GenericViewSet
)允许需要HTML的请求。最简单的方法是将生成HTML的渲染器添加到renderer_classes
。如果您返回HttpResponse
或从HttpResponse
派生的内容, Django REST框架将绕过渲染器。因此您只能使用它来允许需要HTML响应,但不是实际呈现任何内容。
flag
flag
未设置
如果您在 renderers.JSONRenderer
之后添加HTML渲染器,那么您的代码将处理flag
未正确设置的情况。如果JSON和HTML都可以,JSON将获胜,因为它的渲染器在列表中较早。因此,如果request.accepted_renderer.media_type != "application/json"
只是因为客户端不允许JSON响应。
flag
设置
您需要在设置flag
的处理案例中添加一些逻辑,以便拒绝请求,除非HTML是可能的响应格式。请记住,如上所述,如果客户端同时允许JSON和HTML,并且JSON渲染器是渲染器列表中的第一个,则默认情况下将首选JSON。因此,您必须手动检查查询是否允许 HTML。这是一个例子。关键代码在html_possible
方法中:
class FooViewSet(mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
queryset = SomeModel.objects
serializer_class = SomeModelSerializer
lookup_field = "pk"
renderer_classes = (renderers.JSONRenderer, renderers.StaticHTMLRenderer)
def retrieve(self, request, *args, **kwargs):
flag = request.GET.get('flag', None)
if flag:
if not self.html_possible(request):
raise NotAcceptable
return HttpResponseRedirect(SomeModel.objects.get(pk=kwargs["pk"])
.get_absolute_url())
if request.accepted_renderer.media_type != "application/json":
raise NotAcceptable
return super(FooViewSet, self).retrieve(request, *args, **kwargs)
def html_possible(self, request):
from rest_framework.utils.mediatypes import media_type_matches
conneg = self.get_content_negotiator()
accepts = conneg.get_accept_list(request)
return any(media_type_matches(accept,
renderers.StaticHTMLRenderer.media_type)
for accept in accepts)
html_possible
做的是获取内容协商器实例,使用它来获取接受的媒体类型列表,并确定它们中是否有任何一个允许我们返回HTML。如果是,那么我们就可以了。
您可以想象,避免创建谈判者的实例,并自己实施conneg.get_accept_list
。这是非常简单的代码,但我更愿意依赖DRF的代码,以防将来对该逻辑进行一些更改。