03-序列化详细

序列化常见字段

和models里面的字段是一一对应的,知识多出了两个字段

多出了俩 ListField DictField

字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(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"
IPAddressField IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)

序列化常见字段参数

作用: 用来做反序列化校验的。

通用字段

required	表明该字段在反序列化时必须输入,默认为True
defatult	反序列化时使用的默认值
allow_null	表明该字段是否允许传入None,默认False
validators	验证字段的合法性
erorr_message 包含错误变化于错误信息的字典
label		用于HTML展示API页面时,显示的字段名称
help_text	用于HTML展示API页面时,显示的字段帮助提示信息


--------- 非常重要 ----------
read_only	表明该字段仅用于序列化输出,默认False,一般是给前端数据的时候使用
write_one	表明该字段仅用于反序列化输入,默认False 一般用于往数据库里面存放数据

# CharField
max_length	最大长度
min_length	最小长度
allow_length	是否允许为空
trim_withspace	是否截断空白字符

创建表

# models.py
from django.db import  models

class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name="书籍名称")
    price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="书籍价格")
    publish_date = models.DateField(verbose_name="出版日期")
    
    publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外键,关联出版社,设置级联更新和级联删除")
    # 数据库中不会有这个字段。
    # 会生成第三张表 authors
    authors = models.ManyToManyField(to="Authors")
    
    class Meta:
        db_table = "book"
    

class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    addr = models.CharField(max_length=32, verbose_name="出版社地址")
    
    class Meta:
        db_table = "publish"
    
    

class Authors(models.Model):
    name = models.CharField(max_length=32, verbose_name='作者姓名')
    age = models.IntegerField(verbose_name="作者的年龄")
    
    class Meta:
        db_table = "authors"

验证合法性 validators 字段自己的校验 (很少用到)

会把字段的值,传到指定的函数里面去做校验,如果验证通过正常返回,如果验证失败触发异常。 定义的函数形参名称随意。

# 如何使用
# 在类外面定义一个函数,函数放一个形参,名称可以随意
# 在对应的字段中,validators=[ 创建的函数 ], 比如 name = serializers.CharField(validators=[check_field])

from rest_framework import serializers
from datetime import datetime 
from rest_framework.exceptions import ValidationError

# 自己定义的函数,用于给validators校验
def check_name(text):
    if "sb" in text:
        raise ValidationError("包含不合法的输入 sb ")
    else:
        return text

class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8, validators=[check_name])  # 支持放入多个validators=[check_name, check_length...]
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    # 这里的allow_null如果设置了,一般需要结合默认值一起去设置才会生效
    publish_date = serializers.DateField(allow_null=True, default=datetime.now)

自定义错误信息返回字典 error_message

# 自定义序列化类.py
name = serializers.CharField(max_length=8, min_length=2, error_messages={"max_length": "书名最多8位", "min_length": "书名最少2位"})

# 前端报错信息
{
    "code": 100,
    "msg": {
        "name": [
            "书名最少2位"
        ]
    }
}

返回定制字段

自定义返回的名称 source

image-20240412191317073

# book_name 表示代表显示在前端的字段, source=name 这个name就表示在模型层中建立的字段
book_name = serializers.CharField(source="name")

# 可以跨表查询
publish_name = serializers.CharField(source="publish.name")
publish_addr = serializers.CharField(source='publish.addr')

# 所有的字段,都可以设置转成CharField
# 可以 但是非特殊情况不建议 建议按照规范去操作
# publish_id = serializers.CharField(source="publish.id")
publish_id = serializers.IntegerField(source="publish.id")

ListField 和 DictField

  1. 单个结果用字典,即DictField
  2. 多个结果用列表,即ListField
# 后期想实现如下格式返回的数据
{name:书名,price:价格,publish:{name:出版社名,addr:地址},authors:[{},{}]}


# 方式一:在表模型中定义方法

在模型表中定义方法 需要模型表和序列化类一起配合使用

# 序列化类里面的字段
# 出版社对象详细信息
publish_detail = serializers.DictField()

# 作者详细信息
authors_detail = serializers.ListField()

# 模型层中定义
@property
def publish_detail(self):
    # 这里返回列表还是字段,取决于序列化类的字段定义的是 ListField 还是 DictField
    return {"publish_name": self.publish.name, "publish_addr": self.publish.addr}

@property
def authors_detail(self):
    items = []
    # 因为这里是 通过 manytomany创建的第三张表, authors 是一个对象,要拿全部数据要通过 all
    for author in self.authors.all():
        items.append(model_to_dict(author))

通过此方法返回自定义字段,而不通过source实现

思考?这样操作有什么好处?

  1. 更加灵活的定制要返回的数据,隐藏一些敏感信息,或者增加一些信息
  2. 比如身份证号、银行卡、电话号码、地址等,隐藏部分敏感信息。
# 自定义序列化类
book_name = serializers.CharField()

# 模型层
@property
def book_name(self):
    return  '可爱的作者:' + self.name

image-20240412202704848

代码

# 视图层
from  django.shortcuts import render, HttpResponse
from django.views import View
from .serializers import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from django.forms.models import model_to_dict

class BookView(APIView):
    def get(self, request):
        book_obj = Book.objects.all()
        serializer = BookSerializer(instance=book_obj, many=True)
        return Response({"code": 100, "msg": "查询成功!", "results": serializer.data})
    
    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response({"code": 100, "msg": "新增成功!"})
        else:
            return Response({"code": 100, "msg": serializer.errors})
# 模型层
from django.db import  models
from django.forms.models import model_to_dict

class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name="书籍名称")
    price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="书籍价格")
    publish_date = models.DateField(verbose_name="出版日期")
    # 这里是一个对象,所以可以直接通过 . 获取数据
    publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外键,关联出版社,设置级联更新和级联删除")
    # 数据库中不会有这个字段。
    # 会生成第三张表 authors
    authors = models.ManyToManyField(to="Authors")
    
    class Meta:
        db_table = "book"
        
    
    # 如果要在序列化类使用字段,那么一定要保证模型层中有
    # 所以这里包装成数据属性,这样序列化类可以直接使用,然后渲染给前端
    @property
    def publish_detail(self):
        # 这里返回列表还是字段,取决于序列化类的字段定义的是 ListField 还是 DictField
        return {"publish_name": self.publish.name, "publish_addr": self.publish.addr}
    
    
    @property
    def authors_detail(self):
        items = []
        # 因为这里是 通过 manytomany创建的第三张表, authors 是一个对象,要拿全部数据要通过 all
        for author in self.authors.all():
            items.append(model_to_dict(author))
        
        return items
    
    @property
    def book_name(self):
        return '经典书籍:' + self.name
    

class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    addr = models.CharField(max_length=32, verbose_name="出版社地址")
    
    class Meta:
        db_table = "publish"
    
    
    def __str__(self):
        return self.name
        

class Authors(models.Model):
    name = models.CharField(max_length=32, verbose_name='作者姓名')
    age = models.IntegerField(verbose_name="作者的年龄")
    
    class Meta:
        db_table = "authors"
# 序列化类
from rest_framework import serializers
from datetime import datetime 
from rest_framework.exceptions import ValidationError
from .models import Book

def check_name(book_name):
    if "sb" in book_name:
        raise ValidationError("包含不合法的输入 sb ")
    else:
        return book_name


class BookSerializer(serializers.Serializer):
    # book_name = serializers.CharField(source="name", max_length=8, validators=[check_name])
    # 尝试通过 在模型层定义方法 返回 book_name 不适用 source
    book_name = serializers.CharField()
    price = serializers.DecimalField(max_digits=5, decimal_places=2, error_messages={"max_digits": "最少5为"})
    # 这里的allow_null如果设置了,一般需要结合默认值一起去设置才会生效
    publish_date = serializers.DateField(allow_null=True, default=datetime.now)
    # publish = serializers.CharField(source="publish.id")  这里不需要指定publish.id  不然会报错
    # 如果这样写,会把publish的打印的样子给前端,models可以配合__str__打印描述信息,但是不建议这样
    # 因为是一个对象,我需要里面更多的数据,而不是单单打印一个publish.name (models)设置的__str__   
    # publish = serializers.CharField() 
    
    publish = serializers.CharField()
    
    # 出版社对象详细信息
    publish_detail = serializers.DictField()
    
    # 作者详细信息
    # authors_detail = serializers.ListField()
    
    
    # 可以跨表查询
    # 
    publish_id = serializers.CharField(source="publish.id")
    # publish_id = serializers.IntegerField(source="publish.id")
    publish_name = serializers.CharField(source="publish.name")
    publish_addr = serializers.CharField(source='publish.addr')
    
    
    def create(self, validated_data):
        publish = validated_data.pop("publish")
        book = Book.objects.create(**validated_data, publish_id=publish)
        return book

通过 SerializerMethodField 去定制 只用到序列化类和模型表没有关系

  1. 一定要配合一个方法 get_字段名
  2. 其他的和上面的ListField/DictField一样
# 尝试通过 SerializerMethodField 返回book_name
book_name = serializers.SerializerMethodField()

# 出版社对象详细信息
publish_detail = serializers.SerializerMethodField()

# 作者详细信息
authors_detail = serializers.SerializerMethodField()

def get_publish_detail(self, obj):
    # 这个obj 就是当前序列化的 book 对象
    return model_to_dict(obj.publish)


def get_authors_detail(self, obj):
    # 因为是第三张表 不要忘记 all 拿取所有
    return [model_to_dict(author) for author in obj.authors.all()]


# 也可以自己去定制
def get_book_name(self, obj):
    return "必读经典书籍:" + obj.name

image-20240412210047185

子序列化

  1. 定义一个类,继承serializers.Serializer
  2. 通过名称指定即可:publish_detail = PublisuSerializer(source="publish")
  3. 如果是多条需要指定many=True
    1. 多条,一般是多对多的第三张表
    2. 单条,一般是一对多的第三张表
# 定义子序列化类,一般是有外键关系的 多对多 一对多
class PublisuSerializer(serializers.Serializer):
    # 这下面就写要序列化给前端的字段
    id = serializers.IntegerField()  # 出版社ID
    name = serializers.CharField()  # 出版社名称
    addr = serializers.CharField()  # 出版社地址

class AuthorsSerializer(serializers.Serializer):
    id = serializers.IntegerField()  # 作者ID
    name = serializers.CharField()  # 作者姓名
    small_age = serializers.IntegerField(source="age")  # 作者年龄(重命名为small_age)

class BookSerializer(serializers.Serializer):
    
    # 子序列化 展示出版社详细信息
    # 一样可以通过source去指定名称
    # 拿着 PublisuSerializer 类里面的格式去做序列化
    publish_detail = PublisuSerializer(source="publish")  # 出版社详细信息序列化器
    
    # 子序列化,展示作者详细信息
    authors_detail = AuthorsSerializer(many=True, source="authors")  # 作者详细信息序列化器(多个)

image-20240412212159985

反序列化保存 即 保存前端校验通过的数据

read_only和write_only

  1. 后端传递给前端的数据,使用read_only
  2. 后端反序列化前端传过来的数据,写入到数据库里面,使用write_only
  3. read_only和write_only一起使用
  4. 如果是read_only的字段,不填写前端不会报错必填。
  5. 反序列化的字段,可以随意命名,跟表字段没关系,但是后续保存和修改要对应好才行。
# 注意:
	1 read_only write_only 控制序列化类中某些字段,只用来序列化或反序列化
    2 重写updata和create,保存逻辑,我们自己写
    3 视图类中 serializer = BookSerializer(instance=pk, data=request.data)
    	-后续在序列化类中的update中def update(self, instance, validated_data):
        -instance就是当时给的

image-20240412214843332

代码

# 序列化类.py
from rest_framework import serializers
from datetime import datetime 
from rest_framework.exceptions import ValidationError
from .models import Book
from django.forms.models import model_to_dict


# 定义子序列化类,一般是有外键关系的 多对多 一对多
class PublisuSerializer(serializers.Serializer):
    # 这下面就写要序列化给前端的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    addr = serializers.CharField()

class AuthorsSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    small_age = serializers.IntegerField(source="age")

class BookSerializer(serializers.Serializer):
    book_name = serializers.CharField(source="name")
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish_date = serializers.DateField(allow_null=True, default=datetime.now)
    
    # 子序列化 展示出版社详细信息
    # 一样可以通过source去指定名称
    publish_detail = PublisuSerializer(source="publish", read_only=True)
    
    # 子序列化,展示作者详细信息
    authors_detail = AuthorsSerializer(many=True, source="authors", read_only=True)
    
    # 反序列化 保存数据 
    # 这里的publish 是models的一对多外键 前端传入数字 会自动根出版社的id做对应
    publish = serializers.IntegerField(write_only=True)
    # 多对多 是列表 所以要使用ListField
    authors = serializers.ListField(write_only=True)
    

    def create(self, validated_data):
        # {'name': '天龙八部', 'price': Decimal('89.00'), 'publish_date': datetime.datetime(2024, 4, 12, 22, 18, 5, 785495), 'publish': 1, 'authors': [1]}
        publish_id = validated_data.pop("publish")
        authors = validated_data.pop("authors")
        
        # 先获取对象
        book = Book.objects.create(**validated_data, publish_id=publish_id)
        
        # 向中间表插入数据
        book.authors.add(*authors)
        
        return book

	
    # 更新1 前端传入的是 pk
    def update(self, instance, validated_data):

        publish_id = validated_data.pop("publish")
        authors = validated_data.pop("authors")
        
        # 下面的方法会报错 因为uodate返回的并不是qs对象
        # book = Book.objects.filter(pk=instance).update(**validated_data, publish_id=publish_id)
        # book.authors.set(*authors)  # AttributeError: 'int' object has no attribute 'authors'
        
        book_qs = Book.objects.filter(pk=instance) # 找到这个qs对象
        book_qs.update(**validated_data, publish_id=publish_id) # 使用qs去更新
        obj = book_qs.first()
        obj.authors.set(authors)
        
    # 更新2 前端传入的是对象
    # def update(self, instance, validated_data):
    #     publish_id = validated_data.pop("publish")
    #     authors = validated_data.pop("authors")
        
    #     for attr, value in validated_data.items():
    #         setattr(instance, attr, value)
        
    #     instance.publish_id = publish_id
    #     instance.authors.set(authors)
    #     instance.save()
    #     return instance
# views.py
from  django.shortcuts import render, HttpResponse, get_object_or_404
from django.views import View
from rest_framework import status
from .serializers import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from django.forms.models import model_to_dict


class BookView(APIView):
    def get(self, request):
        book_obj = Book.objects.all()
        serializer = BookSerializer(instance=book_obj, many=True)
        return Response({"code": 100, "msg": "查询成功!", "results": serializer.data})

    
    # 新增一本书
    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response({"code": 100, "msg": "新增成功!"})
        else:
            return Response({"code": 100, "msg": serializer.errors})
    
    
class BookDetailView(APIView):
    def get(self, request, pk):
        obj = Book.objects.filter(pk=pk).first()
        serializer = BookSerializer(instance=obj)
        return Response({"code": 100, "msg": serializer.data})
    
    
    def delete(self, request, pk):
        instance = get_object_or_404(Book, pk=pk)
        instance.delete()
        return Response({"code": 100, "msg": "删除成功!"}, status=status.HTTP_204_NO_CONTENT)
    
    
    def put(self, request, pk):
        # obj = Book.objects.filter(pk=pk).first()  如果要修改,因为这里是多表关联,所以可以不用传入对象,传入pk操作更方便
        # 传入的instance是什么,到了update中,就是什么。
        serializer = BookSerializer(instance=pk, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response({"code": 100, "msg": "修改成功!"})
        else:
            return Response({"code": 100, "msg": serializer.errors})

ModelSerializer的使用 重要!

  1. 新增和更新,不需要再写了,不过字段要一一对应,不能修改,默认模型表定义的什么字段,序列化类里面就要什么字段,(看write_only)
  2. 不过如果是新增或者修改,instance不能传入pk了,只能传入对象。
  3. 局部钩子和全局钩子跟之前一样(注意层级),不要写到Meta里面去了,需要和Meta同级。
# views.py
def put(self, request, pk):
    obj = Book.objects.filter(pk=pk).first()  # 如果使用serializers.ModelSerializer 这里只能传入一个对象
    serializer = BookSerializer(instance=obj, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response({"code": 100, "msg": "修改成功!"})
    else:
        return Response({"code": 100, "msg": serializer.errors})
# models.py
from django.db import  models
from django.forms.models import model_to_dict

class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name="书籍名称")
    price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="书籍价格")
    publish_date = models.DateField(verbose_name="出版日期")
    # 这里是一个对象,所以可以直接通过 . 获取数据
    publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外键,关联出版社,设置级联更新和级联删除")
    # 数据库中不会有这个字段。
    # 会生成第三张表 authors
    authors = models.ManyToManyField(to="Authors")
    
    class Meta:
        db_table = "book"

class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    addr = models.CharField(max_length=32, verbose_name="出版社地址")
    
    class Meta:
        db_table = "publish"
    
    
    def __str__(self):
        return self.name
        

class Authors(models.Model):
    name = models.CharField(max_length=32, verbose_name='作者姓名')
    age = models.IntegerField(verbose_name="作者的年龄")
    
    class Meta:
        db_table = "authors"
# 自定义序列化类.py
from rest_framework import serializers
from datetime import datetime 
from rest_framework.exceptions import ValidationError
from .models import Book
from django.forms.models import model_to_dict

# 定义子序列化类,一般是有外键关系的 多对多 一对多
class PublisuSerializer(serializers.Serializer):
    # 这下面就写要序列化给前端的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    addr = serializers.CharField()

class AuthorsSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    small_age = serializers.IntegerField(source="age")

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book # 要关联的表
        fields = "__all__"  # 要关联哪些字段
        # 如果要关联指定的字段 加入到一个列表里面 列表里面放进去字段
        # 这个字段需要用引号包裹起来
        # fields = ["name", "publish" ...]  # 
        
        # 做控制,有的使用序列化,有的使用反序列化
        extra_kwargs = {
            "publish": {"write_only": True},
            "name": {"max_length": 8}, # 控制参数
            "authors": {"write_only": True}
        }
        
    # 如果有自己额外的子序列化字段,需要class Meta 同级新增, 不要写到 Meta 内部了!!!
    publish_detail = PublisuSerializer(read_only=True, source="publish")
    authors_detail = AuthorsSerializer(read_only=True, source="authors", many=True)
    
posted @ 2024-04-13 00:51  小满三岁啦  阅读(8)  评论(0编辑  收藏  举报