drf day03
今日内容详细
一、序列化类常用字段类和字段参数
1. 常用字段类
重点掌握:① CharField
② BooleanField
③ IntegerField
④ DecimalField
DecimalField的使用
类DecimalField(max_digits=8, decimal_places=2)
固定精度的十进制数,在Python中表示一个十进制的实例,有两个必需的参数:
#1. DecimalField max_digits 数中允许的最大数目的数字
#2. DecimalField decimal_places 存储的小数位数的位数


ListField:{name:'lqz',age:19,hobby:['篮球','足球']}
DictField:{name:'lqz',age:19,wife:{'name':'刘亦菲','age':33}}
2. 常用字段参数(字段类的属性)
① 选项参数:
给CharField字段类使用的参数
| max_length | 最大长度 |
| min_length | 最小长度 |
| allow_blank | 是否允许为空 |
| trim_whitespace | 是否截断空白字符 |
给IntegerField字段类使用的参数
| max_value | 最大值 |
| min_value | 最小值 |
②通用参数:放在哪个字段类上都可以的
| required | 表明该字段在反序列化是必须输入(不填就报错),默认true |
| default | 反序列化时使用的默认值 |
| allow_null | 表明该字段是否允许传入None,默认False |
| validators | 该字段使用的验证器【不需要了解】 |
| error_messages | 包含错误编号与错误信息的字典 |
| label | 用于HTML展示API页面时,显示的字段名称(不用看,相当于没有) |
| help_text | 用于HTML展示API页面时,显示的字段帮助提示信息(不用看,相当于没有) |
重点记的通用参数:
| read_only | 表明该字段仅用于序列化输出,默认False |
| write_only | 表明该字段仅用于反序列化输入,默认False |


二、序列化高级用法之source,修改序列化字段名字(不常用)
1. 获取所有图书接口 APIView+Response+序列化类
代码实现流程+页面展示流程











2. 需求:让name字段在前端显示的时候名字重命名为book_name
① 可以使用source字段参数,可以指定序列化表中的哪个字段
book_name = serializers.CharField(max_length=8, min_length=3,source='name')
② source指定的可以是字段,也可以是方法,用于重命名
③ source可以做跨表查询
-序列化字段:




-序列化方法:



-跨表查询:


三、序列化类高级用法之定制序列化字段的两种方式
-表模型和数据录入:
1. 先写多个表,让表与表之间都有联系
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField(null=True)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.name
# 写了个方法,可以包装成数据属性,也可以不包
def publish_de(self):
return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
def author_li(self):
res_list = []
for author in self.authors.all():
res_list.append({'id': author.id, 'name': author.name, 'age': author.age})
return res_list
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
def __str__(self):
return self.name
class AuthorDetail(models.Model):
telephone = models.BigIntegerField()
birthday = models.DateField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
# def __str__(self):
# return self.name
-写完表之后数据库迁移
2. 因为有外键的关系存在,在db.sqlite或者Navicat中不好添加数据,所以我们在admin中注册各个字段名

-向地址127.0.0.1:8000/admin/ 发送get请求:进入到后台管理创数据

-然后我们来创建一个超级用户来登录:
创建超级用户: createsuperroot
创建用户名:wyn
邮箱地址:3156@qq.com
密码:123
确认密码:123

-登录上刚刚创建的用户名和密码就进入了








# 前端显示形式要求变成以下这个样子
{
"name": "西游记",
"price": 33,
"publish": {name:xx,city:xxx,email:sss}
}
# 第一种:在【序列化类】中写SerializerMethodField
publish = serializers.SerializerMethodField()
def get_publish(self, obj):
# obj 是当前序列化的对象(就是book对象)
return {'name': obj.publish.name, 'city': obj.publish.city, 'email': obj.publish.email}
# 第二种:在【表模型】中写方法(又多一些)
def publish_detail(self):
return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
在序列化中取
publish_detail=serializers.DictField()
# 在模型类中写逻辑代码,称之为ddd,领域驱动模型
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(min_value=10, max_value=99)
publish_date = serializers.DateField()
# publish要序列化成 {name:北京出版社,city:北京,email:2@qq.com}
# 方式一:SerializerMethodField必须配合一个方法(get_字段名,需要接受一个参数),方法返回什么,这个字段就是什么
publish = serializers.SerializerMethodField()
def get_publish(self, obj):
# obj 是当前序列化的对象
return {'name': obj.publish.name, 'city': obj.publish.city, 'email': obj.publish.email}
# return返回什么,publish就是什么
# 练习,用方式一,显示所有作者对象 []
authors = serializers.SerializerMethodField()
def get_authors(self, obj):
res_list = []
for author in obj.authors.all():
res_list.append({'id': author.id, 'name': author.name, 'age': author.age})
return res_list
# 表模型中
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField(null=True)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
# 写了个方法,可以包装成数据属性,也可以不包
def publish_detail(self):
return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
def author_list(self):
res_list = []
for author in self.authors.all():
res_list.append({'id': author.id, 'name': author.name, 'age': author.age})
return res_list
# 序列化类中
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=3)
price = serializers.IntegerField(min_value=10, max_value=99)
publish_date = serializers.DateField()
# 方式二:在表模型中写方法
publish_detail = serializers.DictField(read_only=True)
# 练习,使用方式二实现,显示所有作者
author_list = serializers.ListField(read_only=True)

# 反序列化用的
publish = serializers.CharField(write_only=True) # 这两个字段前端是看不到的,因为做了反序列化
authors = serializers.ListField(write_only=True)
# 序列化用的
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# # 序列化和反序列化的名字也是不一样的
# 2 一定要重写create 【序列化类中】
def create(self, validated_data):
# validated_data 校验过后的数据
{"name":"三国1演义",
"price":19,
"publish_date": "2022-09-27",
"publish":1,
"authors":[1,2]
}
book = Book.objects.create(name=validated_data.get('name'),
price=validated_data.get('price'),
publish_date=validated_data.get('publish_date'),
publish_id=validated_data.get('publish'),
)
authors = validated_data.get('authors')
book.authors.add(*authors)
return book
### 这样写很麻烦

# 缺点
1 在序列化中每个字段都要写,无论是序列化还是反序列化
2 如果新增或者修改,在序列化类中都需要重写create或update
# 解决这个缺点,使用ModelSerializer来做
class BookModelSerializer(serializers.ModelSerializer): #ModelSerializer继承Serializer
# 不需要写字段了,字段从表模型映射过来
class Meta: # 这个是内部类
model = Book # 要序列化的表模型 意思是,Book这张表和BookModelSerializer这个序列化类建立映射关系
# fields='__all__' # 所有字段都序列化
fields = ['name', 'price', 'publish_date', 'publish', 'authors', 'publish_detail',
'author_list'] # 列表中有什么,就是序列化哪个字段
# 给authors和publish加write_only属性
# name加max_len属性
extra_kwargs = {
'name': {'max_length': 8},
'publish': {'write_only': True},
'authors': {'write_only': True}, # 字典内写的会当做字段参数传到字段类里面去
}
publish_detail = serializers.SerializerMethodField(read_only=True)
...
author_list = serializers.SerializerMethodField(read_only=True)
总结:
# 如何使用
1 定义一个类继承ModelSerializer
2 类内部写内部内 class Meta:
3 在内部类中指定model(要序列化的表)
4 在内部类中指定fields(要序列化的字段,写__all__表示所有,不包含方法,写[一个个字段])
5 在内部类中指定extra_kwargs,给字段添加字段参数的
6 在序列化类中,可以重写某个字段,优先使用你重写的
name = serializers.SerializerMethodField()
def get_name(self, obj):
return 'sb---' + obj.name
7 以后不需要重写create和update了
-ModelSerializer写好了,兼容性更好,任意表都可以直接存
# 跟forms很像
-字段自己的校验规则
-局部钩子
-全局钩子
# 字段自己的校验规则
-如果继承的是Serializer,写法如下
name=serializers.CharField(max_length=8,min_length=3,error_messages={'min_length': "太短了"})
-如果继承的是ModelSerializer,写法如下
extra_kwargs = {
'name': {'max_length': 8, 'min_length': 3, 'error_messages': {'min_length': "太短了"}},
}
# 局部钩子
-如果继承的是Serializer,写法一样
-如果继承的是ModelSerializer,写法一样
def validate_name(self, name):
if name.startswith('sb'):
# 校验不通过,抛异常
raise ValidationError('不能以sb卡头')
else:
return name
# 全局钩子
-如果继承的是Serializer,写法一样
-如果继承的是ModelSerializer,写法一样
def validate(self, attrs):
if attrs.get('name') == attrs.get('publish_date'):
raise ValidationError('名字不能等于日期')
else:
return attrs
代码实现:
##### 反序列化字段校验
from rest_framework.exceptions import ValidationError
class BookModelSerializer(serializers.ModelSerializer):
name=serializers.CharField(max_length=8,min_length=3)
class Meta:
model = Book
fields = ['name', 'price', 'publish_date', 'publish', 'authors', 'publish_de', 'author_li'] # 列表中有什么,就是序列化哪个字段
extra_kwargs = {
'name': {'max_length': 80, 'min_length': 3, 'error_messages': {'min_length': "太短了"}},
'price': {'max_value': 100, 'min_value': 9},
'publish': {'write_only': True},
'authors': {'write_only': True},
}
# 给name字段加局部钩子,一定不要写在内部类中
def validate_name(self, name):
if name.startswith('sb'):
# 校验不通过,抛异常
raise ValidationError('不能以sb卡头')
else:
return name
def validate_price(self, price):
if price == 44:
raise ValidationError('不能是44,没有为什么')
else:
return price
def validate(self, attrs):
if attrs.get('name') == attrs.get('publish_date'):
raise ValidationError('名字不能等于日期')
else:
return attrs
# 先校验字段自己的规则(最大,最小),走局部钩子校验,走全局钩子
# 局部:validate_name,全局叫:validate 为什么?
# 入口:从哪开始看,哪个操作,执行了字段校验ser.is_valid()
-BaseSerializer内的is_valid()方法
def is_valid(self, *, raise_exception=False):
if not hasattr(self, '_validated_data'):
try:
# 真正的走校验,如果成功,返回校验过后的数据
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
return not bool(self._errors)
-内部执行了:self.run_validation(self.initial_data)---》本质执行的Serializer的
-如果你按住ctrl键,鼠标点击,会从当前类中找run_validation,找不到会去父类找
-这不是代码的执行,代码执行要从头开始找,从自己身上再往上找
def run_validation(self, data=empty):
#局部钩子的执行
value = self.to_internal_value(data)
try:
# 全局钩子的执行,从根上开始找着执行,优先执行自己定义的序列化类中得全局钩子
value = self.validate(value)
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
-全局钩子看完了,局部钩子---》 self.to_internal_value---》从根上找----》本质执行的Serializer的
def to_internal_value(self, data):
for field in fields: # fields:序列化类中所有的字段,for循环每次取一个字段对象
# 反射:去self:序列化类的对象中,反射 validate_字段名 的方法
validate_method = getattr(self, 'validate_' + field.field_name, None)
try:
# 这句话是字段自己的校验规则(最大最小长度)
validated_value = field.run_validation(primitive_value)
# 局部钩子
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
return ret
# 你自己写的序列化类---》继承了ModelSerializer---》继承了Serializer---》BaseSerializer---》Field
# 框架的源码中,大量使用断言
# assert :断言,作用的判断,断定一个变量必须是xx,如果不是就报错
# 你的土鳖写法
# name = 'lqz1'
# if not name == 'lqz':
# raise Exception('name不等于lqz')
#
# print('程序执行完了')
# assert的断言写法
name = 'lqz1'
assert name == 'lqz', 'name不等于lqz'
print('程序执行完了')

浙公网安备 33010602011771号