且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Django REST Framework如何将上下文添加到ViewSet中

更新时间:2023-01-13 12:43:49

虽然我原则上不同意喜悦,但我同意他的观点,即附加的上下文数据应该从串行器发出。这似乎是最简单的方式,因为序列化程序将返回一个本地的Python数据类型,所有的渲染器都将知道如何呈现。



它的外观如何: / p>

ViewSet:

  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)
#在此处访问self.context以添加contex tual 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):
#在这种情况下,我们无法真正调用super,因为我们需要修改内部工作位
renderer_context = renderer_context或{}
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')

如果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, render_context)
如果response.exception:
data ['status_code'] = response.status_code
data.update(render_context)
返回数据

为了在上下文中添加数据,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'
返回上下文

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


The ViewSets do everything that I want, but I am finding that if I want to pass extra context to a template (with TemplateHTMLRenderer) then I will have to get at the functions that give responses.. (like list(), create(), etc)

The only way I can see to get into these is to completely redefine them in my ViewSet, but it seems that there should be an easy way to add a bit of context to the Template without having to redefine a whole set of methods...

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')

Right now my code is looking like this but I will be wanting to add different context to the responses and I am trying to avoid redefining an entire method for such a small change. like this...

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")

Although I disagree with 'pleasedontbelong' in principle, I agree with him on the fact that the extra contextual data ought to be emitted from the serializer. That seems to be the cleanest way since the serializer would be returning a native python data type which all renderers would know how to render.

Heres how it would look like:

ViewSet:

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

Serializer:

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

Now, foo should be available inside your template.

Another way to achieve this, in case you don't wish to mess with your serializers, would be to create a custom 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

To add data into the context, ViewSets provide a get_renderer_context method.

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'} should now be available in your template.