一、MySQL环境小知识点
- Django4+不再支持MySQL5.7,推荐使用 MySQL8.0
- 连接mysql两种库
pip install mysqlclient- 使用
pymysql:需要额外写两行配置代码
- PATH环境变量:命令执行时,系统会按PATH里面的路径依次查找可执行程序。
二、反序列化校验(Serializer校验三层)
serializer.is_valid()执行校验;校验通过数据存到validated_data;失败存到serializer.errors
1)字段本身校验(写在字段参数上)
age=serializers.IntegerField(min_value=1,max_value=110)
name=serializers.CharField(max_length=8,min_length=3)
school=serializers.CharField(max_length=8,min_length=3)
2)局部钩子:validate_字段名(self,value)
只针对某一个字段做自定义校验
-
value:字段经过基础校验之后的值 - 校验不通过:抛出
ValidationError("错误提示") - 校验通过:必须把
valuereturn返回
from rest_framework.exceptions import ValidationError
def validate_name(self,value):
# 不能姓刘
if value.startswith('刘'):
raise ValidationError('你不配姓刘')
return value
3)全局钩子:validate(self, attrs)
针对多个字段联合校验
-
attrs:字典,所有字段完成字段校验、局部钩子校验后的干净数据 - 校验失败抛
ValidationError - 校验通过必须return
attrs
def validate(self, attrs):
# 人名前2字符和学校前2字符不能相同
name = attrs.get('name')[:2]
school = attrs.get('school')[:2]
if name == school:
raise ValidationError("人名和学校的前两个字符不能一样")
return attrs
✅校验执行顺序:字段参数校验 → 局部钩子
validate_xxx→ 全局钩子validate
StudentSerializer完整示例(普通Serializer,不是ModelSerializer)
⚠️普通Serializer必须手动写
create()、update();ModelSerializer不用手写。
class StudentSerializer(serializers.Serializer):
age=serializers.IntegerField(min_value=1,max_value=110)
name=serializers.CharField(max_length=8,min_length=3)
school=serializers.CharField(max_length=8,min_length=3)
def create(self, validated_data):
return Student.objects.create(**validated_data)
def update(self, instance, validated_data):
for k,v in validated_data.items():
setattr(instance,k,v)
instance.save()
return instance
# 局部钩子
def validate_name(self,value):
if value.startswith("刘"):
raise ValidationError("你不配姓刘")
return value
# 全局钩子
def validate(self, attrs):
name = attrs.get('name')[:2]
school = attrs.get('school')[:2]
if name == school:
raise ValidationError("人名和学校的前两个字符不能一样")
return attrs
views.py
class StudentView(APIView):
def post(self,request):
ser = StudentSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
三、定制返回格式(序列化输出,给前端看)
1. source参数
作用:序列化字段映射模型的其他属性(普通字段、模型方法、@property属性)
# 1.映射模型普通字段
sch = serializers.CharField(source="school")
# ⚠️坑:字段名不能和source指向名字重名!
# school=serializers.CharField(source="school") 报错!
# 2.跨表取值(外键对象的属性)
# publish是外键,取publish对象的name
publish_name = serializers.CharField(source="publish.name")
# 3.读取模型@property装饰的方法
# models
# @property
# def new_school(self):
# return "NB_"+self.school
sch = serializers.CharField(source="new_school")
2. SerializerMethodField 【重点】
序列化类中新增模型不存在的自定义字段;
语法:字段名 = serializers.SerializerMethodField()
配套方法必须叫 get_字段名(self,obj)
-
obj:当前正在序列化的模型对象,在方法内做逻辑,return什么,字段就输出什么。
publish_detail = serializers.SerializerMethodField()
def get_publish_detail(self,obj):
return {"name":obj.publish.name,"addr":obj.publish.addr}
3. 一对多、多对多关系三种定制返回方案
模型:Book(图书),Publish(出版社,一对多外键),Author(作者,多对多)
| 方案 | 说明 |
|---|---|
方案1:模型写@property
|
在models写装饰属性,序列化用DictField/ListField接收 |
方案2:SerializerMethodField
|
在序列化类写get_xxx方法,写逻辑拼接字典列表,工作最常用
|
| 方案3:子序列化 | 单独写子序列化类;多对多必须加many=True
|
子序列化示例(多对多)
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
authors = AuthorSerializer(many=True) #多对多,many=True不能丢
四、多表关联反序列化(新增带外键/多对多)
场景:新增图书,前端传
publish_id(出版社主键)、authors:[1,2]作者id列表
反序列化接收主键ID,不能直接用子序列化(子序列化多用于序列化输出)
models
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
publish = models.ForeignKey(to="Publish",on_delete=models.SET_NULL,null=True)
authors = models.ManyToManyField(to="Author")
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
serializer.py
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=32)
price = serializers.IntegerField()
publish_id = serializers.IntegerField() #接收外键id
authors = serializers.ListField() #接收作者id列表 [1,2]
def create(self, validated_data):
# validated_data: {name:"xxx",price:88,publish_id:1,authors:[1,2]}
authors = validated_data.pop("authors") # 先把多对多数据弹出来
book = Book.objects.create(**validated_data) #保存一对多外键
book.authors.add(*authors) #多对多添加中间表记录
return book
views.py
class BookView(APIView):
def post(self,request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({"code":100,"msg":"创建成功"})
return Response(ser.errors)
重要细节
- 前端传:
{"name":"三国演义","price":88,"publish_id":1,"authors":[1,2]} - 前端多传序列化类没有定义的字段,会直接丢弃,不会进入
validated_data - 多对多新增:先
pop()拿出列表,创建主表对象,再用.add(*列表)添加多对多关系。
📌重点记忆总结
-
校验三层:字段参数校验 → 局部钩子
validate_字段名→ 全局钩子validate;钩子校验失败抛ValidationError。 -
source:映射模型字段、跨表、模型@property方法;字段名不能与source目标重名。 -
SerializerMethodField():自定义输出字段,配套get_xxx(self,obj),obj是序列化对象。 - 关联查询返回:三种方案:模型@property、SerializerMethodField、子序列化(多对多要
many=True)。 -
多表反序列化新增:接收id,
pop出多对多数据;obj.add(*ids)保存多对多中间表。 - 普通
Serializer必须手写create/update;ModelSerializer自动生成。
课后作业
- 搭建MySQL8环境
- 敲完source、三种定制返回格式代码
- 实现多表关联修改(put)
- 练习:Author-AuthorDetail、Book完整5个接口,增加自定义校验。
如果你需要,我可以把这一节的完整可运行整套代码整理一份给你直接复制测试。