Published in:2024-10-24 |

drf-day3

1. 知识回顾补充

回顾

1
2
3
4
5
6
7
8
9
10
1. request: 获取请求中所包含的参数
2. render: 决定了数据以何种的形式返回到前端
3. parser: 指定了视图可以接受前端传递什么类型的参数
4. 异常的处理: 解析了drf的异常时如何进行处理的
5. Response: 指定了响应请求的状态码
6. 序列化器: 将从数据库中查询的数据转换成json字符串来响应到前台
序列化: 自定义属性: SerializerMethodField自定义字段
反序列化: 将从前端获取的数据保存至数据库
对前端提交的数据进行安全校验
在保存对象时重写`create()`方法来完成对象的保存

钩子函数

在使用create()方法去保存数据之前,可使用DRF提供的全局钩子以及局部钩子对数据进行自定义校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class EmployeeDeSerializer(serializers.Serializer):
"""
反序列化: 将前端提交的数据保存到数据库
1. 需要前端提供哪些字段
2. 对字段进行安全校验
3. 有没有字段需要额外的校验
反序列化不需要自定义字段
"""

# 添加校验规则
username = serializers.CharField(
max_length=3,
min_length=2,
error_messages={
"max_length": "长度太长了",
"min_length": "长度太短了",
}
)
password = serializers.CharField()
phone = serializers.CharField()

re_pwd = serializers.CharField()

# TODO 钩子函数 在create方法执行之前,DRF提供了两个钩子函数来对数据进行校验

# 全局钩子 获取所有需要序列化的字段
def validate(self, attrs):
# 验证两次密码是否一致
pwd = attrs.get("password")
re_pwd = attrs.pop("re_pwd")

# 自定义规则 两次密码不一致则不能创建员工
if pwd != re_pwd:
raise exceptions.ValidationError("两次密码不一致")

return attrs

# 局部钩子 对某一个字段进行验证
def validate_username(self, value):

# 自定义用户名的校验规则
if "1" in value:
raise exceptions.ValidationError("用户名有误")

# 如果用户名存在 则不能保存
emp = Employee.objects.filter(username=value)
if emp:
raise exceptions.ValidationError("用户名已存在")

return value

# 如果想要完成对象的新增 必须重写create方法
# self是序列化器对象 validated_data需要保存的数据
def create(self, validated_data):
# print(validated_data.get("phone"))
return Employee.objects.create(**validated_data)

2. 模型设计

表设计

1603938320536

抽象表(基表)

可以把多张表共有的字段抽取出来作为基表,

1
2
3
4
5
6
7
8
9
10
class BaseModel(models.Model):
is_delete = models.BooleanField(max_length=128)
create_time = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=True)

class Meta:
# 原元数据中声明此字段后 不会再数据库为此表创建对应的表结构
# 其他模型在继承此模型后,可以继承表中的字段
abstract = True

3. 序列化器之ModelSerializer

ModelSerializer使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
# 指定当前序列化器类要序列化的模型
model = Book

# 指定我要序列化的字段
# fields = ("book_name", "price", "pic")

# 可以直接序列化所有字段
# fields = "__all__"

# 指定不展示哪些字段
exclude = ("is_delete", "status", "create_time")

# 指定查询的深度
# depth = 1

ModelSerializer自定义字段

在序列化器要序列化的模型中提供类方法作为序列化器的自定义属性来展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Book(BaseModel):
book_name = models.CharField(max_length=128)
price = models.DecimalField(max_digits=6, decimal_places=2)
pic = models.ImageField(upload_to="img", default="img/1.jpg")
publish = models.ForeignKey(to="Press", on_delete=models.CASCADE, db_constraint=False,
related_name="books")
authors = models.ManyToManyField(to="Author", db_constraint=False, related_name="books")

class Meta:
db_table = "bz_book"
verbose_name = "图书"
verbose_name_plural = verbose_name

def __str__(self):
return self.book_name

# @property
# def aaa(self):
# return "aaa"

# 自定义序列化属性
@property
def press_name(self):
return self.publish.press_name

@property
def author_list(self):
return self.authors.values("author_name", "age", "detail__phone")

序列化多表查询

  1. 序列化器嵌套夺表查询时, 必须指定的外键的名称,且需要早fields中使用它
  2. 如果嵌套的序列化器不是外键,则无法完成查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class PressModelSerializer(serializers.ModelSerializer):
class Meta:
model = Press
fields = ("press_name", "pic", "address")


class BookModelSerializer(serializers.ModelSerializer):
# 不推荐使用这种自定义的方式
# aaa = serializers.SerializerMethodField()
#
# def get_aaa(self, obj):
# return "aaa"

# TODO 自定义连表查询 查询图书时可以将图书对应的出版社的信息查询出来
# 在一个序列化器内可以嵌套另外一个序列化器类来完成多表查询
# 序列化器对应的字段必须是当前模型类中的外键
publish = PressModelSerializer()

class Meta:
# 指定当前序列化器类要序列化的模型
model = Book

# 指定我要序列化的字段
fields = ("book_name", "price", "pic", "publish")

4. ModelSerializer反序列化

反序列化器的定义

ModelSerializer内部自己实现了create()方法去保存对象, 无需开发者自己重写

extra_kwargs: 可以通过此参数为反序列化器添加校验规则

全局钩子局部钩子同样可以自定义校验规则

fields中所标注的字段必须提供对应的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class BookDeModelSerializer(serializers.ModelSerializer):
"""反序列化器"""

class Meta:
model = Book
fields = ("book_name", "price", "publish", "authors")

# 添加DRF提供的默认校验规则
extra_kwargs = {
"book_name": {
"required": True, # 必填字段
"min_length": 2, # 最小长度
"error_messages": {
"required": "图书名必须提供",
"min_length": "图书名不能少于两个字符",
}
},
"price": {},
}

def validate(self, attrs):
print(attrs)
return attrs

def validate_book_name(self, obj):
print(obj)
return obj

序列化器与反序列化器整合

fields需要填写参与序列化与反序列化的字段的并集

extra_kwargs中可以通过read_onlywrite_only来指定字段只参与序列化或者只参与反序列化

全局钩子与局部钩子仍然可以正常使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class BookModelSerializerV2(serializers.ModelSerializer):
class Meta:
model = Book
# fields应该写哪些字段 应该填写序列化与反序列化所需字段的并集
fields = ("book_name", "price", "publish", "authors", "pic")

extra_kwargs = {
"book_name": {
"required": True, # 必填字段
"min_length": 2, # 最小长度
"error_messages": {
"required": "图书名必须提供",
"min_length": "图书名不能少于两个字符",
}
},
# 指定某个字段只参与序列化
"pic": {
"read_only": True
},
# 指定某个字段只参与反序列化
"publish": {
"write_only": True
},
"authors": {
"write_only": True
},
}

def validate(self, attrs):
print(attrs)
return attrs

def validate_book_name(self, obj):
print(obj)
return obj

5. 接口API

更新单个对象整体

修改对象时,在调用序列化器验证数据时必须指定instance关键字
在调用serializer.save() 底层是通过ModelSerializer内部的update()方法来完成的更新

可以在序列化器通过重写update()方法来完成自定义更新

更新单个对象局部

更新单个局部只需要指定参数partial=True即可

6. ModelSerializer与Serializer

ModelSerializerSerializer的基础上定制了更多的功能

  1. 在序列化ModelSerializer内部自己实现了create()方法与update()方法,在更新以及新增对象时无需自己实现逻辑
  2. 序列化器数据的校验方式以及字段的自定义方式有所不同

作业

1
2
3
4
5
6
7
8
1. 掌握全局钩子与局部钩子的使用方式 ModelSerializer

2. 掌握常见接口的开发
查询单个 查询所有
删除单个 删除所有
新增单个 新增所有
局部修改单个 整个修改单个
3. Django 抽象表的概念
Prev:
Next: