1-Django - 序列化
before
django3.2 + win10 + python3.9
今天遇到一个同学问这样一个django问题:
代码很简单,从表中取出数据,然后以json的形式返回给前端,但是报了序列化的错。
问题产生的原因就是,django中,从表中取出来的数据都是queryset格式,但json无法序列这种格式,导致的报错。
所以,我把解决方法也贴出来。
这里贴出表结构和路由:
# urls.py
from django.contrib import admin
from django.urls import path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
# models.py
from django.db import models
class User(models.Model):
user = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
def __str__(self):
return self.user
自己插入几条数据就完了。
我们接下来主要看在views.py中怎么处理。
当然再说怎么处理之前,先来做个铺垫,就是遇到json无法序列化的类型怎么办?
那些json.dumps无法序列化的类型,需要我们手动处理,参考:
import json
import decimal
from datetime import date, datetime
class CustomJsonEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, date):
return field.strftime('%Y-%m-%d')
elif isinstance(field, datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, decimal.Decimal):
return float(field)
# 如果有更多json无法序列化的类型,就继续elif手动处理
else: # 其他json能序列化的就调用父类的default方法就完了
return json.JSONEncoder.default(self, field)
data_list = [
{"name": "zhangkai1", "age": 18, "ctime": datetime.now()},
{"name": "zhangkai2", "age": 18, "ctime": datetime.now()},
]
# 默认的json无法序列化时间,日期类型的数据
# print(json.dumps(data_list)) # TypeError: Object of type datetime is not JSON serializable
# 所以需要我们手动处理这些数据
print(json.dumps(data_list, cls=CustomJsonEncoder))
"""
[
{"name": "zhangkai1", "age": 18, "ctime": "2021-12-30"},
{"name": "zhangkai2", "age": 18, "ctime": "2021-12-30"
]
"""
PS:如果不懂那个自定义的类的,可以翻翻官档:https://docs.python.org/zh-cn/3.9/library/json.html?highlight=json#module-json进一步了解下。
方案1
手动处理queryset,整理后返回给前端。
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.core import serializers
from app.models import User
def index(request):
user_query_set = User.objects.all()
user_list = [
{"user": user.user, "pwd": user.pwd, "pk": user.pk}
for user in user_query_set
]
# 因为返回的是列表,所以要加上safe,ensure_ascii让浏览器页面中的中文正常显示
return JsonResponse(user_list, safe=False, json_dumps_params={'ensure_ascii':False})
前端拿到的也是json数据(content-type:application/json):
[
{
"user": "zhangkai",
"pwd": "123",
"pk": 1
},
{
"user": "likai",
"pwd": "123",
"pk": 2
}
]
这种解决方式挺好的,但是也有点麻烦,就是字段多了需要手动一个个写。
方案2
使用django内置的序列化组件serializers,它可以序列化xml
、json
、yaml
类型的数据。
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.core import serializers
from app.models import User
def index(request):
user_query_set = User.objects.all()
data_list = serializers.serialize('json', user_query_set)
# data_list = serializers.serialize('json', user_query_set, fields=("user", "pwd"))
# return HttpResponse(data_list)
# 你要是直接这么返回,也没问题,但是默认返回的数据类型是文本类型content-type:text/html; charset=utf-8
# 而且,这么返回,默认也把表名之类的都返回了,也不太好
"""
[
{"model": "app.user", "pk": 1, "fields": {"user": "zhangkai", "pwd": "123"}},
{"model": "app.user", "pk": 2, "fields": {"user": "likai", "pwd": "123"}}
]
"""
# 返回时,加上content_type表示返回的是json类型的数据,content-type:application/json
# 但依然存在上面的问题
return HttpResponse(data_list, content_type="application/json")
但也不推荐这种方法,这种方法存在跨表查询问题。
方案3
使用json和values结合,也推荐这种方式。
import json
import decimal
from datetime import date, datetime
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.core import serializers
from app.models import User
class CustomJsonEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, date):
return field.strftime('%Y-%m-%d')
elif isinstance(field, datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, decimal.Decimal):
return float(field)
# 如果有更多json无法序列化的类型,就继续elif手动处理
else: # 其他json能序列化的就调用父类的default方法就完了
return json.JSONEncoder.default(self, field)
def index(request):
# user_query_set = User.objects.values()
user_query_set = User.objects.values("user", "pwd") # 你也可以指定返回哪些字段
# 虽然使用values,将数据变成了queryset[{}, {}],但本质上还是个queryset
data_list = list(user_query_set)
data_list = json.dumps(data_list, cls=CustomJsonEncoder) # 遇到无法序列化的字段,自己处理
# 这样返回的就是json类型的数据了content-type:application/json
return HttpResponse(data_list, content_type="application/json")
方案4
使用django自带的模型转字典方法。
import json
from datetime import date, datetime
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.core import serializers
from django.forms.models import model_to_dict
from app.models import User
class CustomJsonEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, date):
return field.strftime('%Y-%m-%d')
elif isinstance(field, datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
# 如果有更多json无法序列化的类型,就继续elif手动处理
else: # 其他json能序列化就调用父类的default方法就完了
return json.JSONEncoder.default(self, field)
def index(request):
user_query_set = User.objects.all()
data_list = [model_to_dict(i) for i in user_query_set]
data_list = json.dumps(data_list, cls=CustomJsonEncoder) # 遇到无法序列化的字段,自己处理
# 这样返回的就是json类型的数据了content-type:application/json
"""
[
{
"id": 1,
"user": "zhangkai",
"pwd": "123"
},
{
"id": 2,
"user": "likai",
"pwd": "123"
}
]
"""
return HttpResponse(data_list, content_type="application/json")
方案5
使用django-restful-framework框架的序列化器进行返回,参考序列化器
that's all, see also: