【问题标题】:Django REST Framework how to add context to a ViewSetDjango REST Framework 如何将上下文添加到 ViewSet
【发布时间】:2017-01-05 06:09:06
【问题描述】:

ViewSets 做我想做的一切,但我发现如果我想将额外的上下文传递给模板(使用 TemplateHTMLRenderer),那么我将不得不使用给出响应的函数..(如 list(),创建()等)

我能看到进入这些的唯一方法是在我的 ViewSet 中完全重新定义它们,但似乎应该有一种简单的方法可以向模板添加一些上下文,而不必重新定义一整套方法...

class LanguageViewSet(viewsets.ModelViewSet):
    """Viewset for Language objects, use the proper HTTP methods to modify them"""
    # TODO: add permissions for this view?
    queryset = Language.objects.all()
    serializer_class = LanguageSerializer
    filter_backends = (filters.DjangoFilterBackend, )
    filter_fields = ('name', 'active')

现在我的代码看起来像这样,但我想为响应添加不同的上下文,并且我试图避免为如此小的更改重新定义整个方法。像这样……

class LanguageViewSet(viewsets.ModelViewSet):
    """Viewset for Language objects, use the proper HTTP methods to modify them"""
    # TODO: add permissions for this view?
    queryset = Language.objects.all()
    serializer_class = LanguageSerializer
    filter_backends = (filters.DjangoFilterBackend, )
    filter_fields = ('name', 'active')

    def list(self, **kwargs):
        """Redefinition of list"""

        ..blah blah everything that list does
        return Response({"foo": "bar"}, template_name="index.html")

【问题讨论】:

  • 你做错了,DRF API 中没有(不应该)模板。上下文被传递给序列化器,然后序列化器数据进入一个渲染器,该渲染器检查请求上的Accept 标头以定义如何渲染数据:json、csv 甚至 HTML
  • 所以从我收集的信息来看,API 应该只是获取数据并按照 DRF 的方式处理它。然后我应该使用另一个带有视图和表单等的应用程序将数据发送到 API实现我想要实现的目标?
  • @pleasedontbelong 我已经完成了该教程和长篇教程,并通读了大部分文档。不过,我仍然没有更接近我的问题的答案。如果 DRF 中不应该有模板,那么为什么它们包含 TemplateHTMLRenderer 以及在响应中传递额外上下文的方法? django-rest-framework.org/topics/html-and-forms
  • 我不同意。 ViewSet 的工作是返回原生 python 数据类型。然后渲染器的工作就是将它渲染成任何需要的方式。 JSON 或 HTML。在很多情况下 REST API 需要在 HTML 模板中返回数据。

标签: python django django-rest-framework


【解决方案1】:

我在 Django Rest Framework (DRF) 3.x 中遇到了同样的问题并以稍微不同的方式解决了问题。我认为没有必要重写 TemplateHTMLRenderer 类上相对复杂的渲染方法,而只需重写更简单的方法 get_template_context(或:早期版本的 DRF 中的 resolve_context)。

程序如下:

  1. 覆盖 ViewSet 上的 get_renderer_context 方法(如前所述):

    def get_renderer_context(self):
        context = super().get_renderer_context()
        context['foo'] = 'bar'
        return context
    
  2. 子类TemplateHTMLRenderer,但只覆盖get_template_context方法而不是整个render方法(render调用self.get_template_context来检索最终的上下文以传递给模板):

    class ModifiedTemplateHTMLRenderer(TemplateHTMLRenderer):
    
        def get_template_context(self, data, renderer_context):
    
        """
        Override of TemplateHTMLRenderer class method to display
        extra context in the template, which is otherwise omitted.
        """
    
        response = renderer_context['response']
        if response.exception:
            data['status_code'] = response.status_code
            return data
        else:
            context = data
    
            # pop keys which we do not need in the template
            keys_to_delete = ['request', 'response', 'args', 'kwargs']
            for item in keys_to_delete:
                renderer_context.pop(item)
    
            for key, value in renderer_context.items():
                if key not in context:
                    context[key] = value
            return context
    

{{ foo }} 现在可作为模板变量使用 - 就像在 get_renderer_context 中添加的所有其他变量一样。

【讨论】:

    【解决方案2】:

    虽然我原则上不同意“pleasedontbelong”,但我同意他的观点,即额外的上下文数据应该从序列化程序中发出。这似乎是最简洁的方式,因为序列化程序将返回一个所有渲染器都知道如何渲染的原生 Python 数据类型。

    下面是它的样子:

    视图集:

    class LanguageViewSet(viewsets.ModelViewSet):
    
        queryset = Language.objects.all()
        serializer_class = LanguageSerializer
        filter_backends = (filters.DjangoFilterBackend, )
        filter_fields = ('name', 'active')
    
        def get_serializer_context(self):
            context = super().get_serializer_context()
            context['foo'] = 'bar'
            return context
    

    序列化器:

    class YourSerializer(serializers.Serializer):
        field = serializers.CharField()
    
        def to_representation(self, instance):
            ret = super().to_representation(instance)
            # Access self.context here to add contextual data into ret
            ret['foo'] = self.context['foo']
            return ret
    

    现在,您的模板中应该可以使用 foo。

    如果您不想弄乱序列化程序,实现此目的的另一种方法是创建自定义 TemplateHTMLRenderer。

    class TemplateHTMLRendererWithContext(TemplateHTMLRenderer):
        def render(self, data, accepted_media_type=None, renderer_context=None):
            # We can't really call super in this case, since we need to modify the inner working a bit
            renderer_context = renderer_context or {}
            view = renderer_context.pop('view')
            request = renderer_context.pop('request')
            response = renderer_context.pop('response')
            view_kwargs = renderer_context.pop('kwargs')
            view_args = renderer_context.pop('args')
    
            if response.exception:
                template = self.get_exception_template(response)
            else:
                template_names = self.get_template_names(response, view)
                template = self.resolve_template(template_names)
    
            context = self.resolve_context(data, request, response, render_context)
            return template_render(template, context, request=request)
    
        def resolve_context(self, data, request, response, render_context):
            if response.exception:
                data['status_code'] = response.status_code
            data.update(render_context)
            return data
    

    为了将数据添加到上下文中,ViewSets 提供了一个get_renderer_context 方法。

    class LanguageViewSet(viewsets.ModelViewSet):
    
        queryset = Language.objects.all()
        serializer_class = LanguageSerializer
        filter_backends = (filters.DjangoFilterBackend, )
        filter_fields = ('name', 'active')
    
        def get_renderer_context(self):
            context = super().get_renderer_context()
            context['foo'] = 'bar'
            return context
    

    {'foo': 'bar'} 现在应该在您的模板中可用。

    【讨论】:

    • 我使用了AdminRenderer 的子类而不是TemplateHtmlRenderer,在那里我通过AdminRendererrender 方法调用了get_context 获得了类似的成功。
    猜你喜欢
    • 2014-05-29
    • 1970-01-01
    • 2015-02-12
    • 2021-08-07
    • 2020-06-07
    • 2018-10-01
    • 2015-09-11
    • 2016-09-04
    • 2015-10-25
    相关资源
    最近更新 更多