如何在Django中不使用模板返回JSON?

93
这与这个问题有关:Django return json and html depending on client python
我有一个用于Django应用程序的命令行Python API。当我通过API访问应用程序时,它应该返回JSON,而在浏览器中应该返回HTML。我可以使用不同的URL来访问不同版本,但如何在 views.py 中渲染HTML模板和JSON到一个模板中?
为了渲染HTML,我将使用:
return render_to_response('sample/sample.html....')

但是如果不使用JSON模板,我该如何做到同样的效果呢?(content-type应该是application/json而不是text/html

什么会决定输出JSON或HTML?

因此在我的views.py中:

if something:
    return render_to_response('html_template',.....)
else:
    return HttpReponse(jsondata,mimetype='application/json')

@Marcin,你基本上告诉他“不要这样做”,却没有给他展示正确方法的例子。这就是这个的用途... - Izkata
@Jimmy,如果是这样的话,你不应该那么快就接受Marcin在其他问题上的答案。至少等一天,可能会有人回答类似于Uku Loskit的答案。 - Izkata
@Izkata:我确实告诉他要使用哪个库。这个问题似乎是为了让别人替他编写代码。 - Marcin
10个回答

144
我认为问题在于你想要的内容已经混淆了。我猜想你并不是真正试图将HTML放入JSON响应中,而是希望可以选择返回HTML或JSON。
首先,你需要了解两者之间的核心区别。HTML是一种呈现格式,更多地涉及如何显示数据而不是数据本身。JSON则相反,它是纯粹的数据 -- 基本上是你有某些Python数据集的JavaScript表示形式。它作为一个交换层,让你能够将数据从应用程序的一个区域(视图)移动到另一个区域(你的JavaScript),这些区域通常没有互相访问权限。
基于这个理解,你不需要 "渲染" JSON ,也不需要使用模板。你只需将正在使用的数据(很可能是你将其传递给模板的上下文)转换为 JSON。如果是自由格式的数据,则可以通过 Django 的 JSON 库 (simplejson) 进行转换;如果是查询集,则可以使用其序列化框架进行转换。 simplejson
from django.utils import simplejson

some_data_to_dump = {
   'some_var_1': 'foo',
   'some_var_2': 'bar',
}

data = simplejson.dumps(some_data_to_dump)

序列化

from django.core import serializers

foos = Foo.objects.all()

data = serializers.serialize('json', foos)

无论哪种方式,您都需要将数据传递到响应中:
return HttpResponse(data, content_type='application/json')

[编辑] 在 Django 1.6 及之前版本中,用于返回响应的代码为
return HttpResponse(data, mimetype='application/json')

[EDIT]: simplejson已从django中删除,您可以使用以下内容:
import json

json.dumps({"foo": "bar"})

或者你可以按照上述方法使用django.core.serializers


谢谢你的澄清。我如何在我的视图中确定响应请求是通过 API 获取的 JSON?请参见问题的编辑。 - Neeran
1
你可以使用 request.is_ajax()。但是这需要设置 HTTP_X_REQUESTED_WITH 头部信息。大多数 JavaScript 库会自动设置,但如果你使用其他类型的客户端,你需要确保它也设置了。或者,你可以在 URL 中传递查询字符串,例如 ?json,然后检查 request.GET.has_key('json'),这可能更加可靠。 - Chris Pratt
19
请注意,Django 1.5现已将simplejson视为已弃用的。请改用import json; json.dumps(data) - Yonatan
1
OP 应该检查 request 对象中的 "Accept" 内容类型协商头。参见:http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html(这是一篇庞大的阅读材料,但可以使用简化的代码示例来演示,并且编写一个不太灵活的系统来处理他们所要求的两种情况并不难)。 - Merlyn Morgan-Graham
14
在我的情况下(Django 1.7),它是content_type='application/json',而不是mimetype。 - Nopik

133

8
在JSON响应的情况下,没有要呈现的模板。模板用于生成HTML响应。JSON是HTTP响应。
然而,您可以在JSON响应中使用模板呈现HTML。
html = render_to_string("some.html", some_dictionary)
serialized_data = simplejson.dumps({"html": html})
return HttpResponse(serialized_data, mimetype="application/json")

我需要先序列化对象吗? - Neeran
simplejson.dumps() 是用于序列化的函数。 - Uku Loskit
谢谢,它完全正常工作。我们也可以使用JSON而不是simplejson @UkuLoskit - Chirag Kanzariya

8

为了在Django 1.9中以JSON格式呈现我的模型,我需要在views.py中执行以下操作:

from django.core import serializers
from django.http import HttpResponse
from .models import Mymodel

def index(request):
    objs = Mymodel.objects.all()
    jsondata = serializers.serialize('json', objs)
    return HttpResponse(jsondata, content_type='application/json')

你可以使用 JsonResponse - MichielB

7

2
这是一个示例,根据请求的Accept头部信息有条件地呈现JSON或HTML。
# myapp/views.py
from django.core import serializers                                                                                
from django.http import HttpResponse                                                                                  
from django.shortcuts import render                                                                                   
from .models import Event

def event_index(request):                                                                                             
    event_list = Event.objects.all()                                                                                  
    if request.META['HTTP_ACCEPT'] == 'application/json':                                                             
        response = serializers.serialize('json', event_list)                                                          
        return HttpResponse(response, content_type='application/json')                                                
    else:                                                                                                             
        context = {'event_list': event_list}                                                                          
        return render(request, 'polls/event_list.html', context)

您可以使用curl或httpie进行测试。
$ http localhost:8000/event/
$ http localhost:8000/event/ Accept:application/json

请注意,我选择不使用JsonResponse,因为这会使模型不必要地重新序列化

2

您还可以按照rfc指定的请求接受内容类型进行检查。这样,您就可以默认呈现HTML,在客户端接受application/json时,无需模板即可在响应中返回JSON。


2
from django.utils import simplejson 
from django.core import serializers 

def pagina_json(request): 
   misdatos = misdatos.objects.all()
   data = serializers.serialize('json', misdatos) 
   return HttpResponse(data, mimetype='application/json')

0
如果您想将结果作为渲染模板传递,您需要加载和渲染一个模板,将其渲染结果传递给json。代码可能如下所示:
from django.template import loader, RequestContext

#render the template
t=loader.get_template('sample/sample.html')
context=RequestContext()
html=t.render(context)

#create the json
result={'html_result':html)
json = simplejson.dumps(result)

return HttpResponse(json)

这样你就可以将渲染后的模板作为json传递给客户端。如果您想要完全替换诸如包含许多不同元素的<div>,这将非常有用。


1
顺便提一下,render_to_string是3个“渲染模板”的语句的快捷方式,自Django 1.0以来就存在。 - Izkata

0
在url.py中添加:
```python from django.views.generic import TemplateView urlpatterns = [ ... path('.well-known/microsoft-identity-association.json', TemplateView.as_view(template_name='verify/microsoft.json', content_type='application/json'), name='microsoftwellknow'), ... ] ```
其中,您的模板(microsoft.json)只是一个json文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接