首页 > 其他分享 >序列化高级用法之定制字段的方式,多表关联之反序列化,反序列化字段校验

序列化高级用法之定制字段的方式,多表关联之反序列化,反序列化字段校验

时间:2023-02-02 23:23:15浏览次数:52  
标签:serializers name 之反 only publish CharField 多表 序列化

序列化高级用法之source(了解)

数据准备

# models.py

from django.db import models

# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

模型表创建好之后进行数据库迁移,录入数据。

序列化定制字段名字

# serializer.py

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 将字段名name修改为real_name,通过source指定原来的字段名
    real_name = serializers.CharField(max_length=32,source='name')
    price = serializers.CharField()

    # 一对多外键字段,通过source可以指定外键字段关联的数据(通过外键用的的方式取值)
    publish = serializers.CharField(source='publish.name')

    # 多对多,source不能用
    authors = serializers.CharField(source='authors.name')

执行结果如下:

序列化高级用法之定制字段的两种方式

要想把数据以下面这种方式返回给前端:

'''
 {
    "name": "西游记",
    "price": "55",
    "publish":{"name":"北京出版社","addr":"北京"}, # 真实显示出版社表中的所有数据
    authors:[{"name":"xxx","phone":"1111"},{"name":"yyy","phone":"12345……"}]   # 真实显示本书的所有作者表中的信息 
}
'''

SerializerMethodField定制

# 定制关联字段的显示形式
    -一对多的,显示字典
    -多对多,显示列表套字典

# serializer.py

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    # 将字段名name修改为real_name,通过source指定原来的字段名
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

    publish = serializers.SerializerMethodField()
    # get_后面的字段必须与序列化的字段名一致,即一定得是publish
    def get_publish(self, obj):  # obj指的是当前序列化的对象,即book对象
        return {'name': obj.publish.name, 'addr': obj.publish.addr} # 可以自己指定要返回的数据

    # 多对多
    author_list = serializers.SerializerMethodField()
    def get_author_list(self, obj):  # obj指的是当前序列化的对象,即book对象
        l = []for author in obj.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
        return l

执行结果如下:

 

在表模型中定制

# models.py

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    @property
    def publish_detail(self):  # self指的是当前book对象
        return {'name': self.publish.name, 'addr': self.publish.addr} # 可以自己指定要返回的数据

    @property
    def author_list(self):  # self指的是当前book对象
        l = []

        for author in self.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
        return l

# serializer.py

from rest_framework import serializers


class BookSerializer(serializers.Serializer):

    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

    publish_detail = serializers.DictField()
    author_list = serializers.ListField()

图解:

 

 

read_only 和 write_only

  • read_only:默认False,设置read_only=True:表明该字段仅用于序列化输出(只能查看,不能修改)
  • write_only:默认False,设置write_only=True:表明该字段仅用于反序列化输入(只能修改,不能查看)
    # name和price,即用来序列化,又用来反序列化,既写又读
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

    # 只用来做序列化,只读, read_only
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用来做反序列化,只写,write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)

 

多表关联反序列化保存

新增图书接口

 首先要明确一点,前端传入的数据,绑定了外键字段的,是根据绑定的id值进行选择相应的数据传入,而不是直接输入一条数据。类似于下面的格式:

{"name": "西游记", "price": "55", "publish": 1,  "authors": [1,2]}

# views.py

from rest_framework.views import APIView
from .serializer import BookSerializer
from rest_framework.response import Response

class BookView(APIView):

    def post(self,request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'新增成功'})
        else:
            return Response({'code':101,'msg':ser.errors})

# serializer.py

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    # name和price,即用来序列化,又用来反序列化,既写又读
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()

   
    # 只用来做反序列化,只写,write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)


    # 新增要重写create方法
    def create(self, validated_data):
    # validated_data 校验过后的数据,{"name": "西游记", "price": "55", "publish": 1,  "authors": [1,2]}
        # 新增一本图书
        book = Book.objects.create(name=validated_data.get('name'),price=validated_data.get('price'),
                                   publish_id=validated_data.get('publish'))

        # 作者表也要关联上
        book.authors.add(*validated_data.get('authors'))  # 将列表打散,相当于book.authors.add(1,2)
        return book

修改图书接口

# urls.py

path('books/<int:pk>/',views.BookDetailView.as_view())

# views.py

class BookDetailView(APIView):
    def put(self,request,pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data,instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'修改成功'})
        else:
            return Response({'code':101,'msg':ser.errors})

# serializer.py

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.Serializer):
    # name和price,即用来序列化,又用来反序列化,既写又读
    name = serializers.CharField(max_length=32)
    price = serializers.CharField()


    # 只用来做反序列化,只写,write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)


    # 修改要重写update
    def update(self, instance, validated_data):
    # validated_data 校验过后的数据,{"name": "西游记", "price": "55", "publish": 1,  "authors": [1,2]}
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')  # 注意数据库中外键字段名会自动加_id

        authors = validated_data.get('authors')
        # 方法1:先清空,再add
        # instance.authors.clear()
        # instance.authors.add(*authors)
     # 方法2:也可以直接用set instance.authors.set(authors) instance.save() return instance

 

反序列化字段校验

总共有4层校验

1. 序列化子内字段自带的校验

name = serializers.CharField(max_length=8, error_messages={'max_length': '最长只有为8位'})

2. 局部钩子

3. 全局钩子

4. validators=[方法,]   (先忽略)

ModelSerializer使用

  • 上面我们使用的Serializer 序列化类,它不是只能为数据库模型类定义, 也可以为非数据库模型类的数据定义 (也就是自己定义的数据进行序列化 : {“name”:“xxx”,“age”:18}),
  • serializer是独立于数据库之外的存在, 需要自己在序列化类中去指定模型类的字段
  • 而如果我们想要使用序列化器对应的是Django的模型类, DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类

ModelSerializer相比较于Serializer提供了一些功能

  • 基于模型类自动生成一系列字段 (也就是不再需要自己手动定义字段)
  • 基于模型类自动为 Serializer 生成 validators,比如 : unique_together
  • 包含默认的create( )update( )的实现, 不再需要重写这两种方法

模型类序列化器的定义

  • model:指明参照哪个模型类
  • fields:指明为模型类的哪些字段生成

代码示例:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book  # 序列化类与表模型Book类建立了关联
        # fields='__all__'    # 序列化所有Book中的字段
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']  # 新增的字段要在这个表中进行注册

        # 定制name反序列化时,最长不能超过8   给字段类加属性---方式一
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True},
                        # publish_detail和author_list是新增的两个字段,只用来序列化,设置read_only
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        # publish和author只用来反序列化,设置write_only
                        }

    # 如果Meta写了__all__ ,就相当于,复制了表模型中的所有字段,放在了这里,做了个映射,由此可有得到方式二
    # name = serializers.CharField(max_length=32)
    # price = serializers.CharField(max_length=32)

    # 定制name反序列化时,最长不能超过8   给字段类加属性---方式二,重写name字段
    name = serializers.CharField(max_length=8)

    # 添加两个新的字段(也可以在表模型中定制)
    publish_detail = serializers.SerializerMethodField(read_only=True)
    # get_后面的字段必须与上面的一致,即一定得是publish
    def get_publish_detail(self, obj):  # obj指的是当前序列化的对象,即book对象
        return {'name': obj.publish.name, 'addr': obj.publish.addr}  

    author_list = serializers.SerializerMethodField(read_only=True)
    def get_author_list(self, obj):  # obj指的是当前序列化的对象,即book对象
        l = []
        print(obj)
        for author in obj.authors.all():
            l.append({'name': author.name, 'phone': author.phone})
            print(l)
        return l

    # 局部钩子和全局钩子跟之前完全一样
    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('不能以sb开头')

        else:
            return name

 

标签:serializers,name,之反,only,publish,CharField,多表,序列化
From: https://www.cnblogs.com/chen-ao666/p/17086455.html

相关文章