How do you access data in the template when using

2020-03-01 23:50发布

I've got a Django Rest Framework ModelViewSet and am trying to use the TemplateHTMLRenderer to display HTML. Following along in the tutorial:

from rest_framework import permissions, renderers, viewsets
from rest_framework.decorators import link

from . import models, serializers
from .permissions import IsOwnerOrReadOnly


class SnippetViewSet(viewsets.ModelViewSet):
    template_name = 'snippet-list.html'
    queryset = models.Snippet.objects.all()
    serializer_class = serializers.SnippetSerializer
    renderer_classes = (renderers.TemplateHTMLRenderer,)
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)

    @link(renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

    def pre_save(self, obj):
        obj.owner = self.request.user

If I add a key in def resolve_context() I can access the model objects in my template that are passed into the RequestContext. If I don't add the data key then I don't know how to access the Snippets.

def resolve_context(self, data, request, response):
    if response.exception:
        data['status_code'] = response.status_code

    #return RequestContext(request, data)  # original source on github
    return RequestContext(request, {'data': data})  # if I add a key I can access it

So I've got to be missing something easy or how I'm expecting this to behave is not how the authors intended?

4条回答
狗以群分
2楼-- · 2020-03-02 00:08

I also met the same question with you, and I also thought so. I came here by Google. I didn't like override "def list(self, request, *args, **kwargs):", because I felt it broke the viewset design idea. After I researched the snippet tutorial and source code in the "site-packages\rest_framework", I got the key, not viewset but "serializer.data". In the "site-packages\rest_framework\serializers.py", I found the class BaseSerializer, i.e., the top base class of ModelSerializer. Its property "data" is defined as follows:

@property
def data(self):
    ... # omitted the function body here, because it didn't care about this solution.
    return self._data

This property data is just the "serializer.data" that is just the response passed to template. So I just overrided the data property in "snippets/serializers.py", and after calling the father's method, set the key for the returned data:

class SnippetSerializer(serializers.ModelSerializer):

    @property
    def data(self):
        return { 'data' : super(serializers.ModelSerializer, self).data } #'data' can be replaced with other wanted name.

    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

OK, use the name 'data' in your template.

查看更多
男人必须洒脱
3楼-- · 2020-03-02 00:09

I subclassed and overrode the method that provides the template context, so that the serializer data is available under data within the template context:

from rest_framework.renderers import TemplateHTMLRenderer


class MyHTMLRenderer(TemplateHTMLRenderer):
    def get_template_context(self, data, renderer_context):
        context = {'data': data}
        response = renderer_context['response']
        if response.exception:
            data['status_code'] = response.status_code
        return context
查看更多
叛逆
4楼-- · 2020-03-02 00:18

I would go this way:

class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)

    def list(self, request, *args, **kwargs):
        response = super(SnippetViewSet, self).list(request, *args, **kwargs)
        if request.accepted_renderer.format == 'html':
            return Response({'data': response.data}, template_name='home.html')
        return response

and use http://127.0.0.1:8000/snippets/.html to get table (or whatever suffix you use).

This way you don't override resolver for each render type.

Other solution would be to just create dedicated view for list action and only use HTML renderer. But then you would have a small code duplication.

查看更多
聊天终结者
5楼-- · 2020-03-02 00:24

Inside the viewset use renderer class

renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)

like above and override the ListModelMixin's list method.

mariodev's answer gives the best example also.

查看更多
登录 后发表回答