Django REST Framework如何向ViewSet添加上下文

时间:2016-08-29 09:07:35

标签: python django django-rest-framework

ViewSets会做我想要的所有事情,但我发现如果我想将额外的上下文传递给模板(使用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")

2 个答案:

答案 0 :(得分:2)

虽然我不同意' joyontbelong'原则上,我同意他的观点,即额外的上下文数据应该从序列化器发出。这似乎是最简洁的方法,因为序列化程序将返回一个本机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

要将数据添加到上下文中,ViewSet提供了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'}现在应该可以在您的模板中使用。

答案 1 :(得分:2)

我遇到了同样的问题,并在Django Rest Framework(DRF)3.x中以稍微不同的方式解决了。我认为没有必要在TemplateHTMLRenderer类上覆盖相对复杂的render方法,但在早期版本的DRF中只有更简单的方法get_template_context(或: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方法而不是整个渲染方法(渲染调用self.get_template_context以检索传递给模板的最终上下文):< / p>

    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
    
  3. {{ foo }}现在可用作模板变量 - 与get_renderer_context中添加的所有其他变量一样。