DRF 序列化类serializer多表

【六】序列化类serializer多表

【0】多表数据准备

from django.db import models

# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=64, verbose_name='书名')
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name='价格')
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, verbose_name='出版社外键')
    authors = models.ManyToManyField(to='Author', verbose_name='作者外键')


class Publish(models.Model):
    name = models.CharField(max_length=64, verbose_name='出版社名字')
    addr = models.CharField(max_length=64, verbose_name='出版社地址')


class Author(models.Model):
    name = models.CharField(max_length=64, verbose_name='作者名字')
    detail = models.ForeignKey(to='AuthorDetail', on_delete=models.CASCADE, verbose_name='详情外键字段')


class AuthorDetail(models.Model):
    phone = models.BigIntegerField(verbose_name='电话号码')
    age = models.IntegerField(verbose_name='年龄')

【1】序列化字段对应表

  • 模型表中的字段和序列化类中的字段都是对应的
字段字段构造方式
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(format=’hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressFieldIPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)

【2】序列化字段参数

(1)通用字段参数

参数名称说明
read_only表明该字段仅用于序列化输出,默认False
write_only表明该字段仅用于反序列化输入,默认False
required表明该字段在反序列化时必须输入,默认True
default反序列化时使用的默认值
validators该字段使用的验证器是个列表,列表中是方法的地址
error_messages包含错误编号与错误信息的字典
label用于HTML展示API页面时,显示的字段名称
help_text用于HTML展示API页面时,显示的字段帮助提示信息

(2)可选部分参数

参数名称作用
max_length字符最大长度
min_lenght字符最小长度
allow_blank字符是否允许为空
trim_whitespace字符是否截断空白字符
max_value数值类型最小值
min_value数值类型最大值

【3】两个重要参数

(1)read_only-序列化

  • 当某个字段被标记为 readonly 时,它意味着在序列化过程中,该字段的值可以被读取并包含在序列化结果中,但在反序列化过程中,该字段的值不能被设置。
  • 换句话说,readonly 字段是只读的,可以从对象中提取数据,但不能将数据写入对象。

(2)write_only-反序列化

  • readonly 相反,writeonly 字段在序列化过程中是忽略的,其值不会被包含在序列化结果中。
  • 但在反序列化过程中,该字段的值可以被设置。这意味着 writeonly 字段是只写的,不能从对象中提取它的值,但可以在创建或更新对象时设置它的值。

【4】序列化-source参数

  • 这个参数用于序列化

(1)指定返回给前端的键名

  • 前面的使用方法中,需要将序列化表的字段名和模型表的字段名统一
  • 在这里,只要添加了source参数那么就可以修改序列化类中的字段名
    • 序列化类中的字段名就是返回给前端键名

image-20240413182703695

  • name = serializers.CharField(source='name')
  • 报错AssertionError: It is redundant to specify source='name' on field 'CharField' in serializer 'BookSerializer', because it is the same as the field name. Remove the source keyword argument.
    • 如果出现这个报错,那就说明这个source字段是多余
    • 即字段名已经是模型表中的字段名了

(2)指定模型表的方法字段

  • source还可以指定方法,当然不指定也是可以,只要序列化类的名字字段可以对应上模型层的方法名就可以

image-20240413183726294

(3)联表查询

  • source还可以联表查询

image-20240413184104048

【5】序列化-联表查询(高级)

(1)SerializerMethodField

  • 在序列化类中还有这个字段SerializerMethodField
    • 自定义给前端的名字 = serializers.SerializerMethodField()

    • def get_自定义给前端的名字(self, obj):

    • 其中obj是当前的对象

# 序列化类
class BookSerializer(serializers.Serializer):
    publish_detail = serializers.SerializerMethodField()

    def get_publish_detail(self, obj):
        return {"id": obj.publish.pk, "name": obj.publish.name, "addr": obj.publish.addr}
# 结果
{
    "publish_detail": {
        "id": 2,
        "name": "南方出版社",
        "addr": "南方"
    }
}

(2)DictField()和ListField()

  • 前面的重心在序列化类中,
    • 用这个两个字段就需要将重心放在模型层了
  • 模型编写方法:可以是一对多还可以是多对多字段,只是在序列化类中使用的字段有所差别,多对多需要需要在序列化层中使用ListField,一对多需要在序列化层中使用DictField

image-20240413190519839

(3)子序列化

  • 在序列化类中是可以使用其他序列化类的

  • 方法一:较复杂

    • 同样的首先使用serializers.SerializerMethodField()
    • 然后定义get_xxx方法,在方法中使用其他的序列化类得到结果,用法和在模型层的用法一致
  • 方法二:简单

    • 直接在序列化来中使用其他序列化类,只需要指定source即可
# 序列化类
from rest_framework import serializers

class AuthorSerializer(serializers.Serializer):
    pk = serializers.IntegerField(source='id')
    name = serializers.CharField()

class PublishSerializer(serializers.Serializer):
    pk = serializers.IntegerField(source='id')
    name = serializers.CharField()
    addr = serializers.CharField()
    
class BookSerializer(serializers.Serializer):
    # 方法二
    publish_detail = PublishSerializer(source='publish')
    # 方法一
    authors_detail = serializers.SerializerMethodField()

    def get_authors_detail(self, obj):
        authors = obj.authors.all()
        authors_ser = AuthorSerializer(instance=authors, many=True)
        return authors_ser.data
  • 结果

image-20240413194900421

【6】反序列化-检验数据

  • 在单表序列化中已经讲过了这里就总结性的叙述
  • 当视图层调用.is_valid()时,开始校验数据
    1. 首先是字段参数校验(比如:max_value、…)
    2. 然后是特殊字段参数validators(这是一个列表,需要传入方法地址)
    3. 局部钩子验证单个字段(validate_<field_name>(self, <file_name>)方法)
    4. 全局钩子验证多个字段(validate(self, obj)方法)

【7】反序列化-保存数据

  • 无论是多表还是单表都需要重写create方法,并返回保存的数据
def create(self, validated_data):
    return obj

(1)多表传入是id

  • 在这个例子中,一本书的创建,需要考虑到多个外键字段

    • 首先是一对多的出版社外键字段,这个字段传入的应该是对应出版社的id,并且是单个数字
    • 然后是多对多的作者外键字段,这个字段传入的应该也是对应作者的id,不过这里是列表
  • 但是在序列化类中,如果直接进行合法数据的打散传入是不对的,因为需要传入的是对象,所以需要先将这些数据弹出,然后另外保存外键信息

from rest_framework import serializers
from book.models import Book

class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    addr = serializers.CharField()


class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    publish_detail = PublishSerializer(source='publish', read_only=True)
    authors_detail = AuthorSerializer(source='authors', many=True, read_only=True)

    publish = serializers.IntegerField(write_only=True)
    authors = serializers.ListField(write_only=True)
    def create(self, validated_data):
        print(validated_data)
        # 首先要弹出无法直接保存的数据
        publish_id = validated_data.pop('publish')
        authors_l = validated_data.pop('authors')
        # 创建书籍
        book_obj = Book.objects.create(**validated_data, publish_id=publish_id)
        # 创建书籍和作者的第三章表
        book_obj.authors.set(authors_l)
        return book_obj
  • 结果

image-20240413205014745

(2)多表传入是信息

  • 这里以作者信息为例
  • 在模型表的创建中,作者信息保存在了两个表中
    • 一个表保存了作者的名字和详情的外键字段
    • 另一个表保存了作者的详细信息
  • 在前端传入的过程中是不可能分开传输的,即先传输名字,在传输其他信息,显示是不合理,所以这里通过一个路由接口,传递所有的信息并创建保存
# 序列化类
from book.models import Book, AuthorDetail, Author

class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    phone = serializers.IntegerField(source='detail.phone')
    age = serializers.IntegerField(source='detail.age')

    def create(self, validated_data):
        print(validated_data) # {'name': 'lucy', 'detail': {'phone': 1594687523, 'age': 22}}
        detail_obj = AuthorDetail.objects.create(**validated_data.pop('detail'))
        author_obj = Author.objects.create(name=validated_data.pop('name'), detail=detail_obj)
        return author_obj
# 模型层
class AuthorAPIView(APIView):
    def post(self, request, *args, **kwargs):
        author_ser = AuthorSerializer(data=request.data)
        if author_ser.is_valid():
            author_ser.save()
            return Response({'code': 100, 'msg': '保存成功', 'data': author_ser.data})
        return Response({'code': 1002, 'msg': '校验失败', 'err': author_ser.errors})
  • 结果

image-20240413213140955

【8】反序列-修改数据

  • 无论是多表还是单表都需要重写update方法,并返回保存的数据
def update(self, instance, validated_data):
    return instance

(1)多表传入是id

  • 在这个例子中,一本书的修改,需要考虑到多个外键字段

    • 首先是一对多的出版社外键字段,这个字段传入的应该是对应出版社的id,并且是单个数字
    • 然后是多对多的作者外键字段,这个字段传入的应该也是对应作者的id,不过这里是列表
  • 但是在序列化类中,如果直接进行合法数据的打散传入是不对的,因为需要传入的是对象,所以需要先将这些数据弹出,然后另外保存外键信息

# 序列化类
from rest_framework import serializers
from book.models import Book, AuthorDetail, Author


class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    phone = serializers.IntegerField(source='detail.phone')
    age = serializers.IntegerField(source='detail.age')

    def create(self, validated_data):
        print(validated_data)  # {'name': 'lucy', 'detail': {'phone': 1594687523, 'age': 22}}
        detail_obj = AuthorDetail.objects.create(**validated_data.pop('detail'))
        author_obj = Author.objects.create(name=validated_data.pop('name'), detail=detail_obj)
        return author_obj


class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    addr = serializers.CharField()


class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    publish_detail = PublishSerializer(source='publish', read_only=True)
    authors_detail = AuthorSerializer(source='authors', many=True, read_only=True)

    publish = serializers.IntegerField(write_only=True)
    authors = serializers.ListField(write_only=True)

    def update(self, instance, validated_data):
        # 首先弹出无法直接保存的数据
        publish_id = validated_data.pop('publish')
        authors_l = validated_data.pop('authors')
        # 修改书籍
        for key, value in validated_data.items():
            setattr(instance, key, value)
        # 修改外键
        instance.publish_id = publish_id
        instance.authors.set(authors_l)
        instance.save()
        return instance

    def create(self, validated_data):
        # 首先要弹出无法直接保存的数据
        publish_id = validated_data.pop('publish')
        authors_l = validated_data.pop('authors')
        # 创建书籍
        book_obj = Book.objects.create(**validated_data, publish_id=publish_id)
        # 创建书籍和作者的第三章表
        book_obj.authors.set(authors_l)
        return book_obj

# 模型层
class BookAPIView(APIView):
    def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        book_obj = Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(instance=book_obj, data=request.data)
        if book_ser.is_valid():
            book_ser.save()  # 需要重写update方法
            return Response({'code': 100, 'msg': '修改成功', 'data': book_ser.data})
        return Response({'code': 1002, 'msg': '修改失败', 'err': book_ser.errors})

    def post(self, request, *args, **kwargs):
        book_ser = BookSerializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()  # 需要重写create放啊
            return Response({'code': 100, 'msg': '保存成功', 'data': book_ser.data})
        return Response({'code': 1002, 'msg': '校验失败', 'err': book_ser.errors})

    def get(self, request, *args, **kwargs):
        book_queryset = Book.objects.all()
        # 使用序列化类
        book_ser = BookSerializer(instance=book_queryset, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'ser_results': book_ser.data})
  • 结果

PixPin_2024-04-13_21-53-00

(2)多表传入是信息

  • 这里以作者信息为例
  • 在模型表的修改中,作者信息保存在了两个表中
    • 一个表保存了作者的名字和详情的外键字段
    • 另一个表保存了作者的详细信息
  • 在前端传入的过程中是不可能分开传输的,即先传输名字,在传输其他信息,显示是不合理,所以这里通过一个路由接口,传递所有的信息并创建保存
# 序列化类
from rest_framework import serializers
from book.models import Book, AuthorDetail, Author

class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    phone = serializers.IntegerField(source='detail.phone')
    age = serializers.IntegerField(source='detail.age')

    def update(self, instance, validated_data):
        print(validated_data)  # {'name': 'tom', 'detail': {'phone': 1234850153, 'age': 99}}
        for key, value in validated_data.pop('detail').items():
            setattr(instance.detail, key, value)
        instance.detail.save()
        instance.name = validated_data.pop('name')
        instance.save()
        return instance

    def create(self, validated_data):
        print(validated_data)  # {'name': 'lucy', 'detail': {'phone': 1594687523, 'age': 22}}
        detail_obj = AuthorDetail.objects.create(**validated_data.pop('detail'))
        author_obj = Author.objects.create(name=validated_data.pop('name'), detail=detail_obj)
        return author_obj
# 模型层
class AuthorAPIView(APIView):
    def post(self, request, *args, **kwargs):
        author_ser = AuthorSerializer(data=request.data)
        if author_ser.is_valid():
            author_ser.save()
            return Response({'code': 100, 'msg': '保存成功', 'data': author_ser.data})
        return Response({'code': 1002, 'msg': '校验失败', 'err': author_ser.errors})

    def get(self, request, *args, **kwargs):
        author_queryset = Author.objects.all()
        # 使用序列化类
        author_ser = AuthorSerializer(instance=author_queryset, many=True)
        return Response({'code': 100, 'msg': '查询成功', 'ser_results': author_ser.data})

    def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        author_obj = Author.objects.filter(pk=pk).first()
        author_ser = AuthorSerializer(instance=author_obj, data=request.data)
        if author_ser.is_valid():
            author_ser.save()  # 需要重写update方法
            return Response({'code': 100, 'msg': '修改成功', 'data': author_ser.data})
        return Response({'code': 1002, 'msg': '修改失败', 'err': author_ser.errors})
  • 结果

image-20240413221603782

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值