写在前面
最近在捣鼓微信小程序,想着用Django做后端,在微信小程序里展示数据。自己在写Django的时候,一般都是直接将QuerySet对象丢给模板,然后去渲染。但是微信小程序中,是不能接受该对象的。这就得自己写一个API返回一个Json数据格式了。
查阅了解
来自 wiki
JSON(JavaScript Object Notation,JavaScript对象表示法,读作/ˈdʒeɪsən/)是一种由道格拉斯·克罗克福特构想和设计、轻量级的数据交换语言,该语言以易于让人阅读的文字为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是JavaScript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。
JSON 数据格式与语言无关。即便它源自JavaScript,但当前很多编程语言都支持 JSON 格式数据的生成和解析。JSON 的官方 MIME 类型是 application/json,文件扩展名是 .json
于是问题自然而然的变成了:如何将Django中的对象转化为Json对象返回。好在Django
提供了一个Serializing Django objects
——— 即序列化对象。然后使用HttpResponse()
方法,返回 json
数据就OK了。
来自 Django文档
Django’s serialization framework provides a mechanism for “translating” Django models into other formats. Usually these other formats will be text-based and used for sending Django data over a wire, but it’s possible for a serializer to handle any format (text-based or not).
大概意思如下:
Django的序列化框架提供了一种将Django模型“翻译”为其他格式的机制。通常,这些其他格式将基于文本,并用于通过网络发送Django数据,但是序列化程序可以处理任何格式(无论是否基于文本)
序列化数据
在Django中,序列化数据十分简单:
from django.core import serializers
data = serializers.serialize("json", SomeModel.objects.all(),ensure_ascii=False,fields=('desc','title'))
ensure_ascii 参数默认为False。这是因为 RFC 要求使用UTF-8,UTF-16或UTF-32表示JSON,建议使用 UTF-8 作为默认值,以实现最大的互操作性。序列化程序设置 sure_ascii = True,从而转义输出,以便结果字符串仅包含 ASCII 字符。fields 参数指定序列化字段的子集,如果没有则序列化所有数据。
更多请参阅 官方文档
使用 HttpResponse
在视图 views.py 中使用 HttpResponse()
返回即可
return HttpResponse(data,content_type="application/json")
浏览器查看:
微信开发者工具中查看:
取得返回的数据只需要这样得到所需值:
var desc = res.data[0].fields.desc
var title = res.data[0].fields.title
使用 JsonResponse
JsonResponse
对象:
JsonResponse(data,encoder=DjangoJSONEncoder,safe=True,json_dumps_params=None,** kwargs)
默认Content-Type标头设置为application/json
data必须是dict
safe默认为True,若设置为False可以传递任何对象进行序列化。
继承自 HttpResponse
注意:
设置 safe=False 否则会报错:
TypeError : In order to allow non-dict objects to be serialized set the safe parameter to False.
当然,你也可以这样写 JsonResponse({‘data’:data}),传入一个字典类型。
return JsonResponse(data,safe=False)
浏览器中查看:
显然,其结果还算差强人意。但是在微信开发者工具中可以看见:其得到的json数据为String类型。我们更希望的是返回 object 类型,这是因为从 String 类型中取出我们期望的 desc
或title
要麻烦的多。
来自 Python 文档
说明
Extensible JSON encoder for Python data structures.
Supports the following objects and types by default:
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float | number |
True | true |
False | false |
None | null |
这张表一目了然,我们序列化后的数据 data
为 str 类型(json编码)。这一点很容易就可以验证。所以我们更期望将其转化成 dict
。当然,不要寄希望于这样构造 dict:{'data':data}
,其作用无异于画蛇添足。可以使用 json.loads(data)
将其解码。于是上面代码就变成:
return JsonResponse(json.loads(data),safe=False)
假设项目
假设我们已经创建了一个Django项目 /blogdemo,并创建了一个应用 /blog。下面进行一些简单配置确保项目可以正常启动。
setting.py
的INSTALLED_APPS
中注册应用
INSTALLED_APPS = [
'blog', # 配置应用
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
urls.py
中配置路由
# blogdemo/blogdemo/urls.py 下
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/',include('blog.urls')), # 路由配置
]
# blogdemo/blog/urls.py 下
urlpatterns = [
path('wxapi/',views.wxapi,name='wxapi'),
]
models.py
中创建简单模型
class Post(models.Model):
auth = models.ForeignKey('Auth', on_delete=models.CASCADE)
title = models.CharField(verbose_name="标题",max_length=50)
# CharField 的 max_length 属性是必须的
desc = models.CharField(verbose_name="简述",max_length=200,null=True,blank=True)
def __str__(self):
return self.title
class Auth(models.Model):
name = models.CharField(verbose_name='名字',max_length=50)
about = models.TextField(verbose_name='关于',null=True,blank=False,default="暂无介绍")
def __str__(self):
return self.name
views.py
中编写视图函数
def wxapi(request):
posts = Post.objects.all()
auth = Auth.objects.all()
all = list(posts) + list(auth)
# 序列化
data = serializers.serialize('json', all, ensure_ascii=False)
return HttpResponse(data,content_type="application/json")
通过上面的配置,基本上就可以启动项目了。点击http://127.0.0.1:8000/blog/wxapi/ 查看
E:\Djangofiles\blogdemo>python manage.py makemigrations # 模型迁移
E:\Djangofiles\blogdemo>python manage.py migrate
E:\Djangofiles\blogdemo>python manage.py runserver # 启动项目
当然,现在页面什么数据都没有,因为还没往数据库中添加数据。可以通过Django提供的shell命令窗口使用api方式添加。也可以创建一个管理员账号,通过配置后台,进行添加。这里就不详细讲了。
总结
HttpResponse
更适合返回 serializers
对象,JsonResponse
更合适返回自定义的 dict 类型数据。从 JsonResponse
源码中不难发现,其中对传入的 data 做了 json.dumps()
处理,而前面使用json.loads处理传入数据,JsonResponse
最后返回的数据其实就和HttpResponse
一样了。是的,没有错,这里我做了个傻子操作。无异于画蛇添足(蠢哭.jpg)。都怪我看到到 json,就想到JsonResponse
。
JsonResponse
部分源代码:
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params)
super().__init__(content=data, **kwargs)