如何在设置查询参数时将REST查询重定向到非REST视图?

时间:2016-12-20 21:18:25

标签: django django-rest-framework

我有一个REST视图集,如果请求带有特定标志,我希望能够执行重定向到非REST视图。我想要:

  1. 我的视图集GET,在请求中设置flag时重定向到另一个视图,否则返回JSON响应。

  2. 如果客户端设置了Accept HTTP,因此可以使用JSON或HTML,我希望如果未设置flag,则返回的格式为JSON; flag时为HTML }已设置。 (也就是说,默认格式应根据是否设置flag而有所不同。)

  3. 唯一有效的响应格式是设置标志时的HTML,以及未设置标志时的JSON。

  4. 这就是我所拥有的:

    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

1 个答案:

答案 0 :(得分:0)

使HTML成为可能的格式

您需要进行设置,以便实现内容协商的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的代码,以防将来对该逻辑进行一些更改。