序列化

一. 序列化初步认识

# 序列化
    后端的数据----转化---->提供给前端


# 反序列化
    前端的数据----转化---->后端接收

二. 基于《serializer》基类

serializer是基类,序列化类都要继承它, 但需要写字段,在 “创建” 和 “修改” 时, 需要重写create和updata方法。

1. 《 序列化 》

序列化文件 (serializer.py)
from rest_framework import serializers
from app01 import models

class PublishSerializer(serializers.Serializer):

   # 因为继承 Serializer,需要写序列化的字段
   id = serializers.IntegerField()
   name = serializers.CharField()
   addr = serializers.CharField()
路由文件(models.py)
from django.urls import path
from app01 import views

urlpatterns = [
    path('books/', views.PublishView.as_view()),
    path('books/<int:pk>', views.PublishDetailView.as_view()),
]
视图层 (views.py)
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from .serializer import PublishSerializer


class PublishView(APIView):
    # 获取单条数据
    def get(self, request):
        publish_list = Publish.objects.all()
        ser = PublishSerializer(instance=publish_list, many=True)  # 如果序列化多条,要many=True
        return Response({'code': 100, 'msg': '查询所有成功', 'results': ser.data})

class PublishDetailView(APIView):
    # 获取多条数据
    def get(self, request, pk):
        publish = Publish.objects.filter(pk=pk).first()
        ser = PublishSerializer(instance=publish)  # 单个不写many=True
        return Response(
            {'code': 100, 'msg': '查询单条成功', 'results': ser.data})

2. 《 反序列化 + 校验 》

一共有三层校验

序列化文件 (serializer.py) 《 其他文件没有改变 》
class PublishSerializer(serializers.Serializer):

    # 第一层校验
    id = serializers.IntegerField()

    # error_messages 不想写这个就对 django 做中文配置, 记得注册 rest_framework。
    name = serializers.CharField((max_length=8, min_length=3, error_messages={
        "max_length": '太长了,不能超过8字符',
        "min_length": '太短了,不能少于3字符'
    })
    addr = serializers.CharField()




    # 第二层校验《局部钩子》 validate_需要校验的字段名(self, 需要校验的字段名):
    def validate_name(self, name):
        # 待校验的前端传入的name的数据
        if name.startswith("sb"):
            # 不行,抛异常
            raise ValidationError('不能以sb开头')
        return name



    # 第三层校验《全局钩子》 attrs: 前端传来的全部字段(一中的全部字段), 第一层没写的字段, attrs中则不会有。
    def validate(self, attrs):
        print(attrs)
        # 多个字段同时校验

        if attrs.get('name')[:3] == attrs.get('addr')[:3]:
            raise ValidationError('出版社名和地址不能一样')
        return attrs

3. 《 反序列化 + 校验 + 保存 》

需要重写create和updata方法。

is_valid() 后下面不要打印 res.data

if res.is_valid():
   print(res.data)  # 这里一定不要打印, 会报错。

# 报错信息: AssertionError: You cannot call `.save()` after accessing `serializer.data`.If you need to access data before committing to the database then inspect 'serializer.validated_data' instead. 

序列化文件 (serializer.py)
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
    # read_only=True  意思是只读,只用来做序列化,不用来做反序列化
    # 如果不写read_only=True,这个字段必传,如果不传,数据校验不过
    # 如果写了read_only=True,这个字段不传
    id=serializers.IntegerField(read_only=True)  # model中Auto,本质就是数字,IntegerField
    name=serializers.CharField(max_length=8,min_length=3,error_messages={'max_length':'太长了'})
    # price=serializers.IntegerField()
    price=serializers.CharField(max_length=1000)  # 写成CharField也能映射成功
 
    def create(self, validated_data):  # 代码一点没少写,甚至多了,好处解耦了,view代码少了
        # validated_data就是校验过后的数据
        # 高级
        book=Book.objects.create(**validated_data)
        # 菜鸡
        # name=validated_data.get('name')
        # price=validated_data.get('price')
        # book = Book.objects.create(name=name,price=price)
        return book  # 一定要return新增的对象
 
    def update(self, instance, validated_data):
        # instance 是要修改的对象
        # validated_data是校验过后数据
        instance.name=validated_data.get('name')
        instance.price=validated_data.get('price')
        instance.save()   # 一定不要忘了保存,才存到数据库
        return instance  # 一定要return新增的对象

    # 利用反射方式:《装13》
    def update(self, instance, validated_data):
        for key in validated_data:
            setattr(instance, key, validated_data.get(key))
        instance.save()
        return instance
views 文件 (serializer.py)
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer

# 图书新增:psot  图书查询所有:get
class BookView(APIView):

    def post(self,request):
        # 反序列化--->传得是data=前端传入的数据request.data
        ser=BookSerializer(data=request.data)
        # 数据校验
        if ser.is_valid():  # forms组件就这么做的
            # 保存-->会报错---->需要在序列化类中重写create方法
            ser.save()
            return Response({'msg':'新增成功','code':100, 'data': ser.data})
        else:
            print(ser.errors)
            return Response({'msg': '数据没有校验通过', 'code': 101, 'err': ser.errors})



# 图书查询一个:get  ,图书修改一个:put  ,图书删除一个:delete
class BookDetailView(APIView):

    def put(self,request,pk):
        # 修改,就有book对象,拿到要修改的对象
        book=Book.objects.filter(pk=pk).first()
        # 使用data的数据,修改book这个对象
        ser = BookSerializer(instance=book,data=request.data)
        if ser.is_valid():
            # 会报错,需要重写序列化类的updata方法
            ser.save()
            return Response({'code':100,'msg':"修改成功", 'data': ser.data})
        else:
            return Response({'code': 101, 'msg': "修改失败", 'err': ser.errors})

三. 自定义还回字段 《基于 serializer 基类》

注意:

1. 当反序列化处理 外键 时, 序列化类 字段需要写成 外键名_id (eg: aothor_id), 保存值时同样用《外键名_id》。

2. 当序列化与反序列化字段不一致时, 需要用 read_onlywrite_only做处理。

1. 方式一

在模型表(models.py)中实现

# models.py
class Book(models.Model):
    name = models.CharField(max_length=60)
    publish = models.CharField(max_length=60)
    author = models.ForeignKey(to='Author', on_delete=models.CASCADE)


    # 1. 重写一个方法,返回自己想要的格式。
    # 这就是自定义字段

    def author_obj(self):
        return {"id": self.author.pk, "name": self.author.name}




# serializer.py

class BookSerializers(serializers.Serializer):
    ...
    # 2. 接收 <1> 中的方法,字段名要和方法名一样。《可以不一样, 但需要用 source 做映射》
    # <1> 中还回什么类型,就用什么类型接收,比如:DictField

    author_obj = serializers.DictField(required=False)

2. 方式二

在序列化器(serializer.py)中实现

class BookSerializers(serializers.Serializer):

    # 在这里接收 <1> 中的方法,需要用到 SerializerMethodField

    author_detail = serializers.SerializerMethodField(read_only=True)

    # 1. 同样写一个方法, 命名为《get__字段名》

    def get_author_detail(self, obj):
        return {"id": obj.author.pk, "name": obj.author.name, "age": obj.author.age + 50}

3. 正反向查询时《补充》

通过 _set 反向查询, 本质就是 ORM 相关操作
eg: 通过反向查询, 查找本作者所有的 book。


"""---------------------------------------models.py---------------------------------------"""

class Book(models.Model):
    name = models.CharField(max_length=60)
    publish = models.CharField(max_length=60)
    author = models.ForeignKey(to='Author', on_delete=models.CASCADE)


class Author(models.Model):
    name = models.CharField(max_length=60)
    age = models.IntegerField()

    # 1. 方式一
    def book_list(self):
        l = []
        print(self)
        for i in self.book_set.all():                            # book_set
            print(i)
            l.append({'name': i.name, 'publish': i.publish})
        return l

"""---------------------------------------serializer.py---------------------------------------"""


class AuthorSerializers(serializers.ModelSerializer):
    class Meta:
        model = Author
        # 《 方式一, 方式二都需要在这里映射 》
        fields = ['name', 'age', 'book_list']



    book_list = serializers.SerializerMethodField()
    # 2. 方式一
    def get_book_list(self, obj):
        l = []
        print('obj')
        for i in obj.book_set.all():
            # print(i)
            l.append({'name': i.name, 'publish': i.publish})
        return l

四. 序列化字段及参数

字段类:跟 models 对应,但是比它多


    # BooleanField
    # NullBooleanField
    # CharField
    # EmailField
    # RegexField
    # SlugField
    # URLField
    # UUIDField
    # IPAddressField
    # IntegerField
    # FloatField
    # DecimalField
    # DateTimeField
    # DateField
    # TimeField
    # ChoiceField
    # FileField
    # ImageField

    以上都是models有的----下面是serializer独有的 # (在自定义还回字段可以用到)
    # ListField
    # DictField
    # PrimaryKeyRelatedField

PrimaryKeyRelatedField

重写字段时,ListField 对应的是一个个 “id”, PrimaryKeyRelatedField 对应的是一个个 “对象”

# ListField
    [1, 2, 3, 4]

# PrimaryKeyRelatedField
    [obj1, obj2, obj3, obj4]

"""---------------使用-----------------------"""
# 1. 前端传入
    courses = [1, 2, 3, 4]

# 2. 后端接收
    # 重写字段 queryset 要与指定的表做对应,many=True 多条时需要指定。
        courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(),many=True)
    # 映射
        class Meta:
            model = Order
            fields = [
                'courses',  # 不是表中字段,需要重写
            ]

字段校验参数:


# CharField
    max_length             # 最大长度
    min_lenght             # 最小长度
    allow_blank            # 是否允许为空
    trim_whitespace        # 是否截断空白字符

# IntegerField
    max_value              # 最小值
    min_value              # 最大值

# 通用参数:
    read_only              # 用于序列化输出(只读),默认False
    write_only             # 用于反序列化输入(只写),默认False
    required               # 反序列化时必须输入 (必填),默认True
    default                # 反序列化时使用的默认值
    allow_null             # 表明该字段是否允许传入None,默认False
    error_messages         # 包含错误编号与错误信息的字典

五. ModelSerializer

1. 继承 ModelSerializer,可以不写字段(因为字段跟表有对应关系),也可以不写update和create

2. 特殊情况可以重写 update 和 create

class BookSerializer(serializers.ModelSerializer):

    # ★★★★ 也可以这样重写字段, 括号 里面的校验规则优先级大于 extra_kwargs 中的优先级, 会导致后者失效。
    name = serializers.CharField()

    # 局部校验钩子
    def validate_name(self, name):
        pass

    # 全局校验钩子
    def validate(self, attrs):
        pass

    # 重写字段
    publish = serializers.SerializerMethodField(read_only=True)  # 只用来做序列化
    def get_publish(self, obj):
        pass

    """缩进关系搞懂"""
    class Meta:
        model = Book  					# 跟Book做对应
        # fields='__all__'				# 序列化和反序列化Book中的所有字段

        # ★★★★  无论模型表中的方法, 还是序列化类中的方法, 都需要映射。
        fields = ['name', 'price', 'publish']		# 只序列化和反序列化某几个字段(映射)

        # 在 extra_kwargs 中写校验规则, 和 Serializer 中直接写在括号中一样。
        extra_kwargs = {
            'name': {'max_length': 8, 'min_length': 3},
            'price': {'max_value': 88},

            # 只读(只做序列化)
            'publish': {'read_only': True}

            # 伪代码
            '要校验的字段名': {'校验名': '校验值'}
        }

六. Serializer 和 ModelSerializer 区别

# 伪代码展示
class MySerializer(Serializer or ModelSerializer):

    """-------------关系映射-------------"""
    # Serializer
    name = serializers.CharField()
    price = serializers.CharField()

    # 重写字段
    publish = serializers.SerializerMethodField(read_only=True)
    def get_publish(self, obj):
        pass



    # ModelSerializer
    class Meta:
    model = Book
    fields = ['name', 'price', 'publish'] # 这里要映射

    # 重写字段
    publish = serializers.SerializerMethodField(read_only=True)
    def get_publish(self, obj):
        pass

    """-------------update create 方法-------------"""
    # Serializer
    需要重写, 因为源码没有这两个方法。

    # ModelSerializer
    不需要重写, 必要时可以重写。

七. 多表序列化(重点)

路由层(models.py)
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>', views.BookDetailView.as_view()),
]
视图层(views.py)
from rest_framework.response import Response
from rest_framework.views import APIView
from app01 import models
from app01 import serialize


class BookView(APIView):
    def get(self, request):
        books = models.Book.objects.all()
        res = serialize.MySerialize(instance=books, many=True)
        print(res.data)
        return Response(res.data)

    def post(self, request):
        print(request.data)
        res = serialize.MySerialize(data=request.data)
        if res.is_valid():
            res.save()
            return Response({"msg": "新增成功!", "code": 100})
        else:
            return Response({"msg": "新增失败!", "code": 101, "err": res.errors})


class BookDetailView(APIView):
    def get(self, request, pk):
        book = models.Book.objects.filter(pk=pk).first()
        res = serialize.MySerialize(instance=book)
        print(res.data)
        return Response(res.data)

    def put(self, request, pk):
        book = models.Book.objects.filter(pk=pk).first()
        res = serialize.MySerialize(instance=book, data=request.data)
        if res.is_valid():                               # 会执行:序列化类字段自己的校验规则,局部钩子,全局钩子
            res.save()                                   # 数据保存
            return Response({"msg": "修改成功!", "code": 100})
        else:
            return Response({"msg": "修改失败!", "code": 101, "err": res.errors})
序列化器(serialize.py)===>(重点)
from rest_framework import serializers
from .models import Book


class MySerialize(serializers.ModelSerializer):
    class Meta:
        model = Book
        # fields = '__all__'
        fields = ('nid', 'name', 'price', 'publish', 'authors', 'publish_detail', 'author_list')
        depth = 0

        extra_kwargs = {
            "publish": {'write_only': True, 'read_only': False},
            "authors": {'write_only': True, 'read_only': False}
        }


    publish_detail = serializers.SerializerMethodField(read_only=True)
    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'city': obj.publish.city}

    author_list = serializers.SerializerMethodField(read_only=True)
    def get_author_list(self, obj):
        author_list = []
        for author in obj.authors.all():
            author_list.append({'name': author.name, 'addr': author.author_detail.addr})
        return author_list

八. 重写字段方法

方式一

重写字段 + 必须配合一个方法。 方法返回啥,该字段就是什么--->该字段只能序列化(只出不进)

# 3. 在 fields 里注册
fields=["publish_detail"]

# 2. 配和方法写字段
publish_detail=serializers.SerializerMethodField(read_only=True)

# 1. 重写一个方法
def get_publish_detail(self,obj):
    # obj就是当前 book对象
    print(obj)

    # 返回一个字段
    return obj.publish.name"""

    # 返回多个字段
    return {'name':obj.publish.name,'city':obj.publish.city}

方式二

在表模型models中写方法,在序列化类中写到fields中

序列化类中:
    """fields"""
    fields=['publish_detail']

模型层:
    class Book(models.Model):
         nid = models.AutoField(primary_key=True)
         name = models.CharField(max_length=32)
         price = models.DecimalField(max_digits=5, decimal_places=2)
         ......

         """方法 1, (普通字段)"""
         def publish_detail(self):
             return {"name": self.publish.name, "email": self.publish.email}       # 还回字段可以多样性


         """方法 2, (选择字段)"""
         # 还回选择序号对应的字段中文名
         def publish_detail(self):
             return self.get_数据库已有的字段名_display()

方式三

对某个字段重写序列化类--(子序列化)

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        # fields = '__all__'
        fields = ['id','name','role','title','signature','image','brief']


class CourseSerializer(serializers.ModelSerializer):

    # 子序列化:通过老师的序列化类来实现序列化
    teacher=TeacherSerializer()
    class Meta:
        model = Course

        fields = [
              ...
              'teacher'
              ...
        ]
posted @ 2023-04-03 17:22  codegjj  阅读(1)  评论(0编辑  收藏  举报