基于django的序列化

论坛 期权论坛 脚本     
匿名技术用户   2021-1-7 02:42   11   0

1.序列化组件

前后端分离不能直接返回对象,json只能序列化列表和字典,所以我们在jango中使用序列化组件。

首先要导入利用我们安装好的djangorestframework(后面简称drf)中的一些模块。

from rest_framework import APIView # 我们重写的cbv继承APIview的一些方法和属性

from rest_framework.serializers import Serializer # 序列化的类,帮助我们使用序列化

from rest_framework.response import Response  # 继承了HttpRespose,1.传字典使其序列化2.将数据返回给前端页面

django自带的序列化组件(不推荐使用):

from django.core import serializers # django自带的序列化组件
from rest_framework.views import APIView

class Books(APIView):
    
    def get(self, request):
        response = {'code':100, 'msg': '查询成功'}
        books = models.Book.objects.all()
        ret = serializers.serialize('json', books)
        return HttpResponse(ret)

得到的结果:

难以控制json返回的信息。

使用drf的序列化组件:

1.新建一个序列化类继承Serializer

2.在类中要序列化的字段

在新建文件app01serializer中序列化要序列化的字段。

from app01 import models
from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    book_name = serializers.CharField()
    price = serializers.CharField()

在视图中使用序列化的类:

1.实例化序列化的类产生的对象,在产生对象的时候,传入需要序列化的对象。

2.对象.data

3.return Response(对象.data)

在视图中:

class Books(APIView):

    def get(self, request):
        response = {'code': 100, 'msg': '查询成功'}
        books = models.Book.objects.all()
        # ret = serializers.serialize("json", books)
        # 如果序列化多条,many=True(是queryset对象就要写)
        # 如果序列化一条,many=False,也可以不写
        bookser = BookSerializer(books, many=True)
        return HttpResponse(bookser)

为何many=true为序列化多条源码分析:

如果many=true执行 cls.many_init(*args, **kwargs).

调用Respose:

from rest_framework.response import Response


class Books(APIView):

    def get(self, request):
        response = {'code': 100, 'msg': '查询成功'}
        books = models.Book.objects.all()
        # ret = serializers.serialize("json", books)
        # 如果序列化多条,many=True(是queryset对象就要写)
        # 如果序列化一条,many=False,也可以不写
        bookser = BookSerializer(books, many=True)
        return Response(bookser.data)  # data并不是一个字符串,是一个对象,

结果:返回一个列表。

[
{
"book_name": "apple",
"price": "12.00"
},
{
"book_name": "banana",
"price": "3.00"
}
]

将数据放到字典中就可以得到一个简易版本的cbv:

class Books(APIView):

    def get(self, request):
        response = {'code': 100, 'msg': '查询成功'}
        books = models.Book.objects.all()
        # ret = serializers.serialize("json", books)
        # 如果序列化多条,many=True(是queryset对象就要写)
        # 如果序列化一条,many=False,也可以不写
        bookser = BookSerializer(books, many=True)
        print(type(bookser.data))
        response['data'] = bookser.data
        return Response(response)

.data为一个对象类型,将这个对象作为字典的值。

高级用法:

source:可以指定字段(name publish.name),可以指定方法

SerializerMethodField搭配方法使用:(get_字段名字)

publish_detail = serializers.SerializerMethodField(read_only=True)

def get_authors(self, obj):
    ret = AuthorSerializer(instance=obj.authors.all(), many=True)
    return ret.data

read_only:反序列化时,不传

write_only:序列化时,不显示

利用source='name5',修改序列化模型表中的字段名(保证安全性,是字段名不暴露给外界)。


class BookSerializer(serializers.Serializer):
    #指定source='name' ,表示序列化模型表中的name字段,重名命为name5(name和source='name'指定的name不能重名)
    name5=serializers.CharField(source='name')

source不仅可以指定字段,还可以指定方法:

#如果要取 出版社的city source='publish.city'
publish=serializers.CharField(source='publish.name')

source不但可以指定一个字段,还可以指定一个方法
book_type = serializers.CharField(source='get_xx_display',read_only=True)

# 在模型层新增一个xx的字段
xx=models.IntegerField(choices=((0,'文学类'),(1,'情感类')),default=1,null=True)
# 这个地方隐藏了一个display的方法。

通过1对多获取数据,和通过多对多获取数据。

序列化出版社的详情,指定SerializerMethodField之后,可以对应一个方法,返回什么内容,publish_detail就是什么内容
    publish_detail=serializers.SerializerMethodField(read_only=True)
     #对应的方法固定写法get_字段名
     def get_publish_detail(self,obj):
         print(type(obj))
         return {'name':obj.publish.name,'city':obj.publish.city}

# 通过类的的对象来拿取数据(对于宽展模型表的字段的修改不太友好)
authors = serializers.SerializerMethodField()
    def get_authors(self, obj):
        return [{'name': author.name, 'age': author.age} for author in obj.authors.all()]    

取数据的第二种方式:

# 将外键所关联的表的字段序列化
class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()
    


    # 不必通过多对多关系,直接查询表的数据
    def get_authors(self, obj):     
        authorser = AuthorSerializer(obj.authors.all(), many=True)
        return authorser.data

wiite_only=True:序列化的字段不显示(页面不显示该字段)

read_only=True 反序列化的时候,该字段不传

2.序列化的两种方式

Serializers:没有指定表模型

source:指定要序列化哪个字段,可以是字段,可以是方法

SerializerMethodField的用法

# 序列化出版社的详情,指定SerializerMethodField之后,可以对应一个方法,返回什么内容,publish_detail就是什么内容
    publish_detail=serializers.SerializerMethodField(read_only=True)
     # 对应的方法固定写法get_字段名
     def get_publish_detail(self,obj):
         return {'name':obj.publish.name,'city':obj.publish.city}

#返回所有作者信息
    authors=serializers.SerializerMethodField(read_only=True)
     def get_authors(self,obj):
         # return [ {'name':author.name,'age':author.age} for author in obj.authors.all()]
         authorser=AuthorSerializer(obj.authors.all(),many=True)
         return authorser.data

ModelSerializers:指定了表模型

class Meta:
model=表模型
#要显示的字段
fields='__all__'
fields=('id','name')或fields=['id', 'name']
#要排除的字段
exclude=('name')
#深度控制
depth=1

重写某个字段:

在Meta外部,重写某些字段,方式同Serializers

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields=('nid','name')
        #不能跟fields同时使用
        # exclude=['name',]
        fields='__all__'
        #深度是1,官方建议不要超过10,个人建议不要超过3
        # depth=1
    # xx=serializers.CharField(source='get_xx_display')
    # authors=serializers.SerializerMethodField()
    # def get_authors(self,obj):
    #     ret=AuthorSerializer(instance=obj.authors.all(),many=True)
    #     return ret.data
    # name=serializers.CharField()

fields 表示显示那几个字段;exclude标识除了指定字段,其他都显示;depth表示跨表查显示,depth=1表示查一张相关联的表的信息(所有信息都会被显示出来),比较难以控制,而且depth查多张表,影响性能。

3.反序列化

Serializers:没有指定表模型

使用继承了Serializers序列化类的对象,反序列化
-在自己写的序列化类中重写create方法
-重写create方法,实现序列化
-在序列化类中:
def create(self, validated_data):
ret=models.Book.objects.create(**validated_data)
return ret
-在视图中:
def post(self,request):
bookser=BookSerializer(data=request.data)
if bookser.is_valid():
ret=bookser.create(bookser.validated_data)
return Response()

ps:继承了Serializers,就要重写create和update方法(方法名不能为save,必须为create或update)因为save会重写继承的save方法:

判断用create方法或update方法:

不能走save方法的原因:

序列化逻辑:

class BookSerializer(serializers.Serializer):
    # source=book_name 修改模型表中的book_name字段名,修改的字段名和指定的字段名不能重复
    name = serializers.CharField(source='book_name')
    price = serializers.DecimalField(max_digits=8, decimal_places=2)
    # book_type = serializers.CharField(source='get_classification_display', write_only=True) # 影响is_valid校验不过去,因为与模型表类型不同,且对应的值不一样
    publish_detail = serializers.SerializerMethodField(read_only=True)
    def get_publish_detail(self, obj):
        return {'name':obj.publish.name, 'city':obj.publish.city}
    # authors = serializers.SerializerMethodField(read_only=True)
    def get_authors(self, obj):
        # return [{'name': author.name, 'age': author.age} for author in obj.authors.all()]
        authorser = AuthorSerializer(obj.authors.all(), many=True)
        return authorser.data

    def create(self, validated_data): # 函数名必须为create
        ret=models.Book.objects.create(**validated_data)
        return ret

视图层逻辑:

class Books(APIView):

    def get(self, request):
        response = {'code': 100, 'msg': '查询成功'}
        books = models.Book.objects.all()
        # ret = serializers.serialize("json", books)
        # 如果序列化多条,many=True(是queryset对象就要写)
        # 如果序列化一条,many=False,也可以不写
        bookser = BookSerializer(books, many=True)
        print(type(bookser.data))
        response['data'] = bookser.data
        return Response(response)

    # 反序列化
    def post(self, request):
        # 实例化产生一个序列化的对象,data是要反序列化的字典
        print(request.data)
        bookser = BookSerializer(data=request.data)
        # print(bookser.is_valid())
        if bookser.is_valid(raise_exception=True):
            bookser.save()
            # 这里的save方法,本质上就是create方法,所以要在序列化的时候重写save方法
        return Response()

ModelSerializers:指定了表模型

使用继承了ModelSerializers序列化类的对象,反序列化
-在视图中:
def post(self,request):
bookser=BookSerializer(data=request.data)
if bookser.is_valid():
ret=bookser.save()
return Response()

ModelSerializers在自己类中写了ceate和update方法,所以我们用的时候不必重写这两个方法。

Ps: 在从postman传传数据的时候,不能根据本表的外键关系,来添加数据,分析发现 is_valid(), 将这个表外键关系对应的表的信息自动过滤,解决这个问题,要我们自己在后端手动将这个id,添加到validdated_data.

4.反序列化的校验

-validate_字段名(self,value):
-如果校验失败,抛出ValidationError(抛出的异常信息需要去bookser.errors中取)
-如果校验通过直接return value


-validate(self,attrs)
-attrs所有校验通过的数据,是个字典
-如果校验失败,抛出ValidationError
-如果校验通过直接return attrs

一般在ModelSerializersz 内指定字段名(不建议使用__all__),使用全局钩子和局部钩子做校验。

#反序列化的校验(局部校验,全局校验)
    def validate_name(self,value):

        print(value)
        raise exceptions.ValidationError('不能以sb开头')
        # if value.startswith('sb'):
        #     raise ValidationError('不能以sb开头')
        # return value

    def validate(self,attrs):
        print(attrs)
        # if attrs.get('price')!=attrs.get('xx'):
        #     raise exceptions.ValidationError('name和price相等,不正常')
        return attrs

因为我们用drf,遵循rest_ful规范,所以后端做校验的时候,在后端抛出异常信息。

#使用继承了ModelSerializers序列化类的对象,反序列化
    def post(self,request):
        #实例化产生一个序列化类的对象,data是要反序列化的字典
        bookser=BookSerializer(data=request.data)
        # bookser.data
        if bookser.is_valid(raise_exception=True):  # 给前端抛出异常
            #清洗通过的数据
            bookser.save()
        else:  # 后端抛出异常
            print(bookser.errors['name'][0])
        return Response()

钩子函数通过is_valid()这个入口函数进行如下源码校验:

校验字段:

5.源码阅读

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP