Python Django REST框架如何向视图集添加上下文
视图集可以做我想做的一切,但是我发现如果我想将额外的上下文传递给模板(使用TemplateHTMLRenderer),那么我必须使用提供响应的函数。。(如list()、create()等) 我能看到的进入这些的唯一方法是在我的ViewSet中完全重新定义它们,但似乎应该有一种简单的方法可以向模板添加一点上下文,而不必重新定义一整套方法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
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