序列化类常用字段和参数,序列化高级用法之定制字段的方式,多表关联之反序列化,反序列化字段校验
序列化类的字段类型和参数
常用字段类型
字段 | 字段构造方式 |
---|---|
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=) |
字段参数
- 针对 CharField 类型
参数 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
- 针对 InterField 类型
参数 | 作用 |
---|---|
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页面时,显示的字段帮助提示信息 |
序列化高级用法之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……"}] # 真实显示本书的所有作者表中的信息 } '''
# 定制关联字段的显示形式 -一对多的,显示字典 -多对多,显示列表套字典
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)
注意:在表的操作中,一般都是既要做序列化,又要做反序列化,一定要指明哪些字段是read_only,哪些字段是write_only。
首先要明确一点,前端传入的数据,绑定了外键字段的,是根据绑定的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())
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
序列化内字段自带的校验
name = serializers.CharField(max_length=8, error_messages={'max_length': '最长只有为8位'})
2. 使用字段参数 validators 来进行校验
- 该参数可以指定校验函数, 适用于多个字段都要进行同一种校验
- 如果每个都写钩子就很麻烦, 于是就可以写一个校验函数, 每个字段都使用 validators 参数来指定该函数
# 定义一个校验函数 def name_start(data): if data.startswith('xxx'): raise ValidationError('不能以"xxx"开头') else: return data # 然后在需要进行该校验的字段中加入 validators 参数指定该校验函数 author = serializers.CharField(validators=[name_start]) ... city = serializers.CharField(validators=[name_start]) ...
3. 局部钩子
validate_[字段名]
# 校验名字不能以"xxx"开头 def validate_name(self, data): # data就是当前字段的值 if data.startswith('xxx'): raise ValidationError('不能以"xxx"开头') else: return data # 返回校验后的值
4. 全局钩子validate
全局钩子: # 校验名字不能与所在城市同名 def validate(self, attrs): name = attrs.get('name') city = attrs.get('city') if name == city: raise ValidationError('名字不能和城市名一样!') else: return attrs # 返回所有数据
- 上面我们使用的Serializer 序列化类,它不是只能为数据库模型类定义, 也可以为非数据库模型类的数据定义 (也就是自己定义的数据进行序列化 : {“name”:“xxx”,“age”:18}),
- serializer是独立于数据库之外的存在, 需要自己在序列化类中去指定模型类的字段
- 而如果我们想要使用序列化器对应的是Django的模型类, DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类
ModelSerializer相比较于Serializer提供了一些功能
- 基于模型类自动生成一系列字段 (也就是不再需要自己手动定义字段)
- 基于模型类自动为 Serializer 生成 validators,比如 : unique_together
- 包含默认的
create( )
和update( )
的实现, 不再需要重写这两种方法
模型类序列化器的定义
model
:指明参照哪个模型类fields
:指明为模型类的哪些字段生成extra_kwargs
: 使用该参数为ModelSerializer添加或修改原有的选项参数 (重点, 更好的方法)
代码示例:
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