drf序列化器

序列化组件介绍

# 序列化组件的作用:序列化;反序列化(包括数据校验)
    1. 序列化, 序列化器会把模型对象转换成字典, 经过response以后变成json字符串,传给前端
    2. 反序列化, 把客户端发送过来的数据, 经过request以后变成字典, 序列化器可以把字典转成模型
    3. 反序列化, 完成数据校验功能

序列化器Serializer

Serializer序列化使用流程

1 写一个序列化的类,继承Serializer
2 在该类中写要序列化的字段,想序列化哪个字段,就在类中写哪个字段
3 在视图类中使用,导入--》实例化得到序列化类的对象,把要序列化的对象传入
4 序列化类的对象.data   是一个字典
5 把字典返回,如果不使用rest_framework提供的Response, 就得使用JsonResponse

	- 序列化,使用instance参数
# urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),	# GET一条/PUT/DELETE
    url(r'^books/', views.BooksView.as_view()),		# GET所有, POST
]

# ser.py
from rest_framework import serializers
from app01 import models

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.DecimalField()
    author = serializers.CharField()
	
   
# views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models, ser, utils

class BooksView(APIView):
    # 获取所有
    	- 序列化
    	- instance参数传入queryset对象,多个对象,使用参数many=True
    def get(self, request):
        book_list = models.Book.objects.all()
        ser_obj = ser.BookSerializer(instance=book_list, many=True)
        back_info_obj = utils.BackInfo()
        back_info_obj.add_param('data', ser_obj.data)
        return Response(back_info_obj.get_info)
	
   
class BookView(APIView):
    # 获取单个
    	- 序列化
        - instance参数传入queryset对象
    def get(self, request, pk):
        back_info_obj = utils.BackInfo()
        book_obj = models.Book.objects.filter(pk=pk).first()
        if book_obj:
            ser_obj = ser.BookSerializer(instance=book_obj)
            back_info_obj.add_param('data', ser_obj.data)
        else:
            back_info_obj.add_error(code='400', message='数据不存在')
        return Response(back_info_obj.get_info)

   
# utils.py,自己封装的返回信息字典类
class BackInfo(object):
    def __init__(self):
        self.code = '100'
        self.message = 'ok'

    def add_param(self, key, value):
        self.__dict__[key] = value
        return self

    def add_error(self, code, message):
        self.reset_info()
        self.code = code
        self.message = message
        return self

    def reset_info(self):
        self.__dict__.clear()
        return self

    @property
    def get_info(self):
        return self.__dict__

Serializer反序列化使用流程

"""
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。
验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。
验证成功,可以通过序列化器对象的validated_data属性获取数据。
在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

"""

# 反序列化校验数据时,每个字段的序列化类型和选项参数是第一道校验关卡
# 第一道校验关卡不够使用,可以使用局部钩子和全局钩子坐第二道校验关卡
	- 局部勾钩子:validate_字段名,接收一个参数(当前字段数据data),需要return data
    - 全局钩子:validate,接收一个参数(全部校验字段参数数据validated_data),需要return validated_data
    - 校验失败,主动抛出异常,raise ValidationError('xxx')
    - from rest_framework.exceptions import ValidationError
    
# 此外,在第一道关卡内还有validator参数,可以使用外部函数做当前字段的校验方式
	- data是当前字段数据,校验成功需要返回data
def check_name(data):
    print('check_name.....')
    return data

class BookSerializer(serializers.Serializer):
    name = serializers.CharField(min_length=3, max_length=10, validators=[check_name,])
  

# 反序列化,保存数据,新增数据时,需要重写父类的create()方法
	def create(self, validated_data):
        book_obj = models.Book.objects.create(**validated_data)
        return book_obj
# 修改数据时,需要重写父类的update()方法
	def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.author = validated_data.get('author')
        instance.save()
        return instance
    
# 某些情况下需要重写save()方法,此时需要在save()方法内直接操作validated_data属性
class ContactForm(serializers.Serializer):
    email = serializers.EmailField()
    message = serializers.CharField()

    def save(self):
        email = self.validated_data['email']
        message = self.validated_data['message']
        send_email(from=email, message=message)
# 反序列化-提交新增记录
    	"""
    	- 反序列化
    	- 参数data=request.data,实例序列化器对象ser_obj,is_valid()校验数反序列化数据
        - 校验成功的数据在ser_obj.data,通过ser_obj.save()保存数据,
        - 此时需要在序列化器类中重写create()方法
    	"""
    def post(self, request):
        back_info_obj = utils.BackInfo()
        # 这个按位置传request.data会给instance就报错了,使用关键字参数data
        ser_obj = ser.BookSerializer(data=request.data)
        if ser_obj.is_valid():
            pass


# 反序列化-修改数据
    	"""
    	- 反序列化
        - 参数instance=book_obj表示待修改的对象, 参数data=request.data表示反序列化的数据
        - 此时需要在序列化类中重写父类的update方法
    	"""
    def put(self, request, pk):
        back_info_obj = utils.BackInfo()
        book_obj = models.Book.objects.filter(pk=pk).first()
        ser_obj = ser.BookSerializer(instance=book_obj, data=request.data)
        if ser_obj.is_valid():
           pass
    
####### save()方法内部根据instance参数,判断调用create()方法还是update()方法。
# instance默认为None,
	- 当给instance参数传入一个模型对象时,这是判断为修改数据的参数,save()内部会调用update()方法。
    - 当instance参数没有传入参数,此时instance=None, 表示新增数据,则save()内部调用create()方法。


# 删除, 不需要使用序列化器

常用字段类型

字段 字段构造方式
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=)

选项参数:

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

通用参数:

参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
# read_only	
	表明该字段仅用于序列化输出,默认False,如果设置成True,postman中可以看到该字段,修改时,不需要传该字段
# write_only	
	表明该字段仅用于反序列化输入,默认False,如果设置成True,postman中看不到该字段,修改时,该字段需要传
    
# read_only和write_only不能同时作用在一个字段上
# 一般默认,表的id字段是read_only=True的,因此不能再设置id字段为write_only=True

模型类序列化器ModelSerializer

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model=Book  # 对应上models.py中的模型
        fields='__all__'
        # fields = ('name','price','id','author') # 只序列化指定的字段
        # exclude = ('name',) #跟fields不能都写,写谁,就表示排除谁
        # read_only_fields = ('price',)
        # write_only_fields = ('id',) # 弃用了,使用extra_kwargs
        extra_kwargs = {  # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
            'price': {'write_only': True},
        }
        
# 其他使用方法和Serializer的使用方法一模一样
# 一般情况下,不需要重写create和updata方法了
# 注意:write_only_fields弃用了,可以使用extra_kwargs
# 在ModelSerializer的子类中,表中不存在的字段可以直接新增一个字段,但是需要在fields中加入新增加的字段。
# 新增模型表中已经存在的字段,会覆盖掉模型表中的该字段。
# 序列化时,depth=1,字表示跨表查数据时的跨表深度。

序列化器的高级用法

#1 source的作用及其使用
	1 可以改字段名字 xxx=serializers.CharField(source='title')
    2 可以.跨表 publish=serializers.CharField(source='publish.email')[基于对象的跨表查询,子查询]
    3 可以执行方法pub_date=serializers.CharField(source='test') test是定义在Book模型类中的方法
    
# test方法
class Book(models.Model):
    pub_date = models.DateField(auto_now_add=True)
   
	def test(self):
        return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.pub_date))
    
    
#2 SerializerMethodField()的使用
	1 它需要有个配套方法,方法名叫get_字段名,返回值就是该字段在序列化时返回给前段显示的东西
    authors=serializers.SerializerMethodField() 
    
    def get_authors(self, instance):
        # 注意instance是当前book对象。
        authors = instance.authors.all()  # 取出所有作者
        return [{'id': author.pk, 'name': autho.name, 'age': author.age} for author in authors]
      
    
# 注意:
	- 如果在模型类序列化器中使用source和SerializerMethodField, 需要重写字段,把class Meta中的fields覆盖掉
    class BookModelSerializer(ModelSerializer):
        class Meta:
            model = models.Book
            # exclude = ('authors', )
            fields = '__all__'
            extra_kwargs = {
                'pub_date': {'read_only': True},
                # 'publish': {'source': 'publish.email'},
            }
        publish = serializers.CharField(source='publish.name')
        authors = serializers.SerializerMethodField()
        def get_authors(self, instance):
            authors = instance.authors.all()  # 取出所有作者
            ll = []
            for author in authors:
                ll.append({'name': author.name, 'age': author.age})
            return ll

跨表操作的ModelSerializer

需求:基于模型类的序列化器完成序列化和反序列化的跨表操作,一对多,多对多操作的增删改查操作

基于ModelSerializer,支持序列化和反序列化操作,使用了source参数和SerializerMethodField字段对象等。

# models.py
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    pub_date = models.DateField()
    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE, null=True)
    authors = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.name


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

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    
    def __str__(self):
        return self.name
    
# ser.py
class BookModelSerializer(serializers.ModelSerializer):
    # publish_id和authors_id两个字段仅用于增加或修改数据时,前段传过来的json数据的key,且他俩是只写字段
    # 注意:authors_id使用的是ListField(多个作者的id列表)
    # authors_id字段中使用了source参数器别名,这是因为模型类内部调用create()或者update()时操作第三张表本质是通过book_obj.authors.add()或者book_obj.authors.set()完成的,调用的是'authors'不是'authors_id'
    publish_id = serializers.IntegerField(write_only=True)
    authors_id = serializers.ListField(write_only=True, source='authors')

    class Meta:
        model = models.Book
        # 这里需要把新增的publish_id和authors_id增加到fields中,不然不能使用
        fields = (
            'id', 'name', 'price', 'pub_date',
            'publish', 'authors',
            'publish_id',
            'authors_id',
        )
        # 指定序列化时,跨表查询深度
        depth = 1
        # 通过extra_kwargs指定publish和authors两个字段是只读字段
        extra_kwargs = {
            'publish': {'read_only': True},
            'authors': {'read_only': True},
        }
        

源码分析

many=True的实际用途

# 序列化多条数据(列表套字典对象),需要传many=True
book_ser=BookModelSerializer(books,many=True)
book_one_ser=BookModelSerializer(book)
print(type(book_ser))
#<class 'rest_framework.serializers.ListSerializer'>
print(type(book_one_ser))
#<class 'app01.ser.BookModelSerializer'>

# 对象的生成--》先调用类的__new__方法,生成空对象
# 对象=类名(name=lqz),触发类的__init__()
# 类的__new__方法控制对象的生成


def __new__(cls, *args, **kwargs):
    if kwargs.pop('many', False):
        return cls.many_init(*args, **kwargs)	#<class 'rest_framework.serializers.ListSerializer'>
    # 没有传many=True,走下面,正常的对象实例化
    return super().__new__(cls, *args, **kwargs)	#<class 'app01.ser.BookModelSerializer'>
posted @ 2020-07-08 00:08  the3times  阅读(231)  评论(0编辑  收藏  举报