Python Django REST框架如何向视图集添加上下文

Python Django REST框架如何向视图集添加上下文,python,django,django-rest-framework,Python,Django,Django Rest Framework,视图集可以做我想做的一切,但是我发现如果我想将额外的上下文传递给模板(使用TemplateHTMLRenderer),那么我必须使用提供响应的函数。。(如list()、create()等) 我能看到的进入这些的唯一方法是在我的ViewSet中完全重新定义它们,但似乎应该有一种简单的方法可以向模板添加一点上下文,而不必重新定义一整套方法 class LanguageViewSet(viewsets.ModelViewSet): """Viewset for Language objects

视图集可以做我想做的一切,但是我发现如果我想将额外的上下文传递给模板(使用TemplateHTMLRenderer),那么我必须使用提供响应的函数。。(如list()、create()等)

我能看到的进入这些的唯一方法是在我的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")

虽然原则上我不同意“PleaseDontAlley”,但我同意他的观点,即额外的上下文数据应该从序列化程序中发出。这似乎是最干净的方式,因为序列化程序将返回所有呈现程序都知道如何呈现的本机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应该在模板中可用

如果您不想弄乱序列化程序,实现这一点的另一种方法是创建自定义模板HtmlRenderer

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
要将数据添加到上下文中,视图集提供了一个
get\u renderer\u 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'}
现在应该可以在您的模板中使用了。

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

程序如下:

  • 覆盖视图集上的
    get\u renderer\u context
    方法(如前所述):

  • 子类
    TemplateHTMLRenderer
    ,但仅覆盖
    get\u template\u context
    方法,而不是整个呈现方法(呈现调用
    self.get\u template\u context
    以检索要传递给模板的最终上下文):


  • {{foo}}
    现在可以作为模板变量使用-正如在
    get\u renderer\u context
    中添加的所有其他变量一样,如果您做得不对,DRF API中没有(不应该有)模板。上下文被传递给序列化程序,然后序列化程序数据进入一个呈现,该呈现检查请求上的
    Accept
    头,以定义如何呈现数据:json、csv,甚至HTMLso。从我收集的数据来看,API应该只是获取数据,并按照DRF的方式处理数据。。然后,我应该使用另一个带有视图和表单等的应用程序将数据发送到API,以实现我想要实现的目标?遵循整个教程@PleaseDontAlley我已经完成了该教程和长表单教程,并阅读了大部分文档。不过,我仍然没有找到我问题的答案。如果DRF中不应该有模板,那么为什么它们包含
    TemplateHTMLRenderer
    以及在响应中传递额外上下文的方法?我不敢苟同。视图集的任务是返回本机python数据类型。然后,渲染器的任务是将其渲染为所需的任何方式。JSON或HTML。REST API需要在HTML模板中返回数据的情况很多。我使用了
    AdminRenderer
    的子类,而不是
    TemplateHtmlRenderer
    ,在这种情况下,我成功地覆盖了
    get_context
    ,这是由
    AdminRenderer
    render
    方法调用的。
    def get_renderer_context(self):
        context = super().get_renderer_context()
        context['foo'] = 'bar'
        return 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