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,它可以序列化xmljsonyaml类型的数据。

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:

Django:序列化的几种方法 | 序列化 serializers

posted @ 2021-12-30 13:27  听雨危楼  阅读(285)  评论(0编辑  收藏  举报