我想调用/contacts/1.json返回json,1.api返回browsableAPI,并调用format = None aka / contacts / 1 /来返回我们调用render_form的模板。这样最终用户可以拥有漂亮的表单,开发人员可以使用.api格式,ajax / apps等使用.json。看起来像是一个常见的用例,但在DRF中没有点击我这里...
在没有给出格式的情况下,DRF如何确定使用的渲染器。我发现然后在堆栈交换中丢失了一些信息,基本上就是根据格式拆分响应。添加TemplateHTMLRenderer会导致各种各样的痛苦。我曾尝试基于格式进行拆分,但这会给我下面的JSON错误。
我不明白定义应该使用哪个渲染器的事实上的方法。特别是当没有提供格式时。我的意思是,当使用Response(数据)时它“正常工作”。我可以使TemplateHTMLRenderer工作,但代价是没有默认的渲染器。
GET / contacts / 1 /给出错误:
<Contact: Contact object> is not JSON serializable
使用此代码:
class ContactDetail(APIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
queryset = Contact.objects.all()
renderer_classes = (BrowsableAPIRenderer, JSONRenderer, TemplateHTMLRenderer,)
"""
Retrieve, update or delete a contact instance.
"""
def get_object(self, pk):
try:
return Contact.objects.get(pk=pk)
except Contact.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
contact = self.get_object(pk)
serializer = ContactSerializer(contact)
if format == 'json' or format == "api":
return Response(serializer.data)
else:
return Response({'contact': contact, 'serializer':serializer}, template_name="contact/contact_detail.html")
但是GET /contacts/1.json,1.api或1.html ALL会给我正确的输出。所以我似乎已经为默认的内容协商创建了一个问题,即format = None
我必须遗漏一些基本的东西。我已经浏览了2 tutorials并阅读了Renderers文档,但我不清楚我在此默认情况下搞砸了什么。我没有在settings.py中使用DEFAULT_RENDERERS,如果在默认情况下或在上面显示的实际类中,似乎没有什么区别。
此外,如果有人知道使用TemplateHTMLRenderer的方法而无需打开格式值,我会全神贯注。
编辑:如果我使用
if format == 'json' or format == "api" or format == None:
return Response(serializer.data)
else:
return Response({'contact': contact, 'serializer':serializer},
然后我会默认显示可浏览的API。不幸的是,我想要的是默认情况下的模板HTML视图,它被设置为显示最终用户的表单。我想为开发人员保留.api格式。
答案 0 :(得分:0)
当我查看源代码时,优先级似乎是settings.py中DEFAULT_RENDERER_CLASSES
参数中指定的渲染器的顺序:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.TemplateHTMLRenderer',
)
}
因此,如果您指定了一堆渲染器类,则将根据给定.json/.api/.html
扩展名和Accept:
标头(不是content-type
,正如我在你的问题评论中所说的那样)。
答案 1 :(得分:0)
<强> TL; DR:检查渲染器的顺序 - 它们按照声明的顺序进行尝试,直到内容协商匹配或发生错误。
更改行
renderer_classes = (BrowsableAPIRenderer, JSONRenderer, TemplateHTMLRenderer, )
到
renderer_classes = (TemplateHTMLRenderer, BrowsableAPIRenderer, JSONRenderer, )
为我工作。我相信原因是因为内容协商器在尝试查找渲染器时从渲染器类元组的第一个元素开始。当我有format==None
时,我认为DRF没有别的东西继续下去,所以我假设我的意思是“默认”,这似乎是元组中的第一个。
编辑:所以,正如@Ross在他的回答中指出的那样,该项目的settings.py中还有一个全局设置。如果我删除了我的班级renderer_classes
声明,而是在settings.py
# ERROR
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.BrowsableAPIRenderer',
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
)
}
然后我得到(不同的)JSON错误。但是,只要 'rest_framework.renderers.BrowsableAPIRenderer', 未列出,例如:
# SUCCESS, even though JSON renderer is checked first
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
因此,如果我们在尝试TemplateHTMLRenderer之前点击BrowsableAPIRenderer,那么我们会收到错误 - 无论我们是否依赖renderer_classes或DEFAULT_RENDERER_CLASSES 。我想它会优雅地通过JSONRenderer,但无论出于何种原因,BrowsableAPIRenderer都会引发异常。
所以我在分析了这个...之后简化了我的视图代码......
def get(self, request, pk, format=None):
contact = self.get_object(pk)
serializer = ContactSerializer(contact)
if format == None:
return Response({'contact': contact, 'serializer':serializer}, template_name="contact/contact_detail.html")
else:
return Response(serializer.data)
..这更好地反映了我原本试图做的事情。