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'>