数据准备
models.py
from django.db import models
# Create your models here.
class Group(models.Model):
title = models.CharField(verbose_name="组名", max_length=32)
class Role(models.Model):
title = models.CharField(verbose_name="角色名", max_length=32)
class User(models.Model):
gender_choice = (
(1, '男'),
(2, '女')
)
username = models.CharField(verbose_name="用户名", max_length=32, unique=True)
password = models.CharField(verbose_name="密码", max_length=32)
gender = models.IntegerField(verbose_name="性别", choices=gender_choice, default=1)
group = models.ForeignKey(to='Group', on_delete=models.CASCADE, verbose_name="用户组")
roles = models.ManyToManyField(to='Role', verbose_name="角色")
插入数据:
两大作用
- 对请求数据的验证
- 对queryset类型数据进行序列化
基本使用
获取所有的角色信息
url 路由
# 总路由
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('app01.urls'), name='api')
]
# app01路由
urlpatterns = [
re_path(r'^(?P<version>[v1|v2]+)/roles/$', views.RoleView.as_view(), name='roles')
]
定义角色相对应的Serializers类, 用于序列化数据, app01/serializers.py
from rest_framework import serializers
class RolesSerializers(serializers.Serializer):
title = serializers.CharField()
从数据库拿到数据,利用RolesSerializers 进行序列化queryset对象数据,app01/views
import json
from django.shortcuts import render, HttpResponse
from app01 import models
# Create your views here.
from rest_framework.views import APIView
from app01.serializers import RolesSerializers
class RoleView(APIView):
def get(self, request, *args, **kwargs):
roles = models.Role.objects.all()
# 对 quertset [obj,obj...] 格式数据的格式化
roles_ser = RolesSerializers(instance=roles, many=True) # 如果拿到的queryset中有多条数据,many=True
print(roles_ser.data) # roles_ser.data 是序列化之后的结果,是一个有序的字典
# 对单个对象的格式化
# roles_ser = RolesSerializers(instance=roles, many=False) # 单个对象,many=False
return HttpResponse(json.dumps(roles_ser.data, ensure_ascii=False))
postman测试,拿到json数据
只需要修改RolesSerializers中的字段信息,就可以拿到其他的数据库表Role的字段值
class RolesSerializers(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField()
自定义字段
获取用户信息,显示出用户的性别(choice字段的显示)
1 url
re_path(r'^(?P<version>[v1|v2])/userinfo/$', views.UserInfoView.as_view(), name='userinfo')
2 序列化类
class UserInfoSerializers(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
gender = serializers.IntegerField()
3 视图函数
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = models.User.objects.all()
users_ser = UserInfoSerializers(instance=users, many=True)
print(users_ser.data)
return HttpResponse(json.dumps(users_ser.data, ensure_ascii=False))
4 访问
所有的性别(gender),都是数字,并不是我们期望的显示 “男”或者“女”,是因为数据库中存储的就是数字类型。
解决方式一:增加source参数,指定模型类中的字段,如果可以执行,就会加括号调用。
class UserInfoSerializers(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
# 给定什么变量名,序列化之后的key就是什么
xingbie = serializers.IntegerField(source='gender') # 指定了source,source的值和对应模型类中的字段名一致,这个类中=号前面的就不需要和模型类中保持一致了。
# source 找到每一行的对象a,执行a.source的值, 即a.gender, 会先判断a.gender是否可以调用,如果可以调用就会加括号执行 a.gender()
gender = serializers.CharField(source='get_gender_display')
获取用户信息,显示出用户所在的组(ForiegnKey)
class UserInfoSerializers(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
# 给定什么变量名,序列化之后的key就是什么
xingbie = serializers.IntegerField(source='gender') # 指定了source,source的值和对应模型类中的字段名一致,这个类中=号前面的就不需要和模型类中保持一致了。
# source 找到每一行的对象a,执行a.source的值, 即a.gender, 会先判断a.gender是否可以调用,如果可以调用就会加括号执行 a.gender()
gender = serializers.CharField(source='get_gender_display')
# 显示组
gp = serializers.CharField(source='group')
组信息序列化之后是一个对象,如果想要拿到组id怎么取?
解决方式一:
在source的值中可以继续点,意思就是source=group.id,会先取第一个值,再点取第二个值,可以一直点下去。
class UserInfoSerializers(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
# 给定什么变量名,序列化之后的key就是什么
xingbie = serializers.IntegerField(source='gender') # 指定了source,source的值和对应模型类中的字段名一致,这个类中=号前面的就不需要和模型类中保持一致了。
# source 找到每一行的对象a,执行a.source的值, 即a.gender, 会先判断a.gender是否可以调用,如果可以调用就会加括号执行 a.gender()
gender = serializers.CharField(source='get_gender_display')
gp = serializers.CharField(source='group.title')
获取用户信息,显示出用户的角色(多对多)
多对多不能通过source做到细粒度的显示,使用另一种方式,自定义显示
class UserInfoSerializers(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
# 给定什么变量名,序列化之后的key就是什么
xingbie = serializers.IntegerField(source='gender') # 指定了source,source的值和对应模型类中的字段名一致,这个类中=号前面的就不需要和模型类中保持一致了。
# source 找到每一行的对象a,执行a.source的值, 即a.gender, 会先判断a.gender是否可以调用,如果可以调用就会加括号执行 a.gender()
gender = serializers.CharField(source='get_gender_display')
gp = serializers.CharField(source='group.title')
rls = serializers.SerializerMethodField() # 自定义显示
def get_rls(self, row): # 函数名命名规则: get+字段名
roles = row.roles.all()
ret = []
for item in roles:
ret.append({'id': item.id, 'title': item.title})
return ret
自定义字段之继承ModelSeria
序列化除了继承Serializer
,还可以继承ModelSerializer
, 后者是前者功能的丰富版。
# 帮我们省去了写字段的步骤
class UserInfoSerializers(serializers.ModelSerializer):
class Meta:
model = models.User
fields = "__all__" # 数据库所有字段
但是 还是有些数据是显示的不尽人意。
解决方式:
class UserInfoSerializers(serializers.ModelSerializer):
gender = serializers.CharField(source='get_gender_display')
rls = serializers.SerializerMethodField() # 自定义显示
class Meta:
model = models.User
# fields = "__all__"
fields = ['id', 'username', 'password', 'gender', 'rls']
def get_rls(self, row): # 函数名命名规则: get+字段名
roles = row.roles.all()
ret = []
for item in roles:
ret.append({'id': item.id, 'title': item.title})
return ret
序列化深度控制
depath 控制序列化的深度,自动序列化连表操作。
你想拿字段的时候,depth
的值是多少,就往里拿几层。depth=1,如果是一对多,多对多,就会往多的在走一层,拿到所有数据。层数越多速度响应率越慢。
如:拿到用户表的数据,depth默认为0
class UserInfoSerializers(serializers.ModelSerializer):
class Meta:
models = models.User
fields = '__all__'
depth = 1
序列化完成后的json数据,group字段是一对多,roles字段是多对多,深度走到了当前表的下一层,如果下一层表中有一对多、多对多,还可以继续往下走,缺点是拿到了下一层的所有字段数据。
[
{
"id": 1,
"username": "张三",
"password": "123",
"gender": 1,
"group": {
"id": 1,
"title": "组1"
},
"roles": [
{
"id": 1,
"title": "经理"
},
{
"id": 2,
"title": "程序员"
}
]
},
...
]
个人建议取值范围为[0,3]之间。
序列化源码分析
- 创建继承了
ModelSerializer
的类 - 实例化类
users_ser = UserInfoSerializers(instance=users, many=True)
- 实例化类的时候会先调用
__new__
,然后调用__init__
方法
3.1BaseSerializer
的__new__
方法
QuerySet对象,交给ListSerializer
类处理
4.ser.data 序列化
BaseSerializer类中的data
5.调用to_representation方法
BaseSerializer
6.执行CharField的get_attribute
走到Field类
具体源码执行流程移步:https://blog.csdn.net/no_name_sky/article/details/120862632
请求数据校验
默认校验
1.新增url
re_path(r'^(?P<version>[v1|v2]+)/group/$', views.GroupView.as_view(), name='gp')
2.编写类
class GroupSerializers(serializers.Serializer):
title = serializers.CharField(error_messages={'required': "标题不能为空"})
3.编写视图函数
class GroupView(APIView):
def post(self, request, *args, **kwargs):
print(request.data)
group_ser = GroupSerializers(data=request.data)
if group_ser.is_valid():
print(group_ser.validated_data)
else:
print(group_ser.errors)
return HttpResponse('提交成功')
4.测试数据校验
自定义字段校验规则
方式一:自定义类
class TitleValidator(object):
def __init__(self, base):
self.base = str(base)
def __call__(self, value):
# 数据一提交过来就会执行,value就是提交过来的值
if not value.startswith(self.base):
message = "必须以{}开头".format(self.base)
raise serializers.ValidationError(message)
class GroupSerializers(serializers.Serializer):
title = serializers.CharField(error_messages={'required': "标题不能为空"}, validators=[TitleValidator('组')]) # validators指定校验规则
方式二:钩子函数
自己看源码找钩子函数是哪个。。
源码
def to_internal_value(self, data):
for field in fields:
validate_method = getattr(self, 'validate_' + field.field_name, None)
钩子函数:
class RolesSerializers(serializers.Serializer):
title = serializers.CharField()
# 局部钩子
def validate_title(self, value):
base = "小老弟"
if not value.startswith(base):
message = "必须以{}开头".format(base)
raise serializers.ValidationError(message)
return value
# 在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证
# def validate(self, attrs):
# if attrs.get('name') == attrs.get('title'):
# raise serializers.ValidationError('city和名字不能一样')
# else:
# return attrs
## 路由
re_path(r'^(?P<version>[v1|v2]+)/roles/$', views.RoleView.as_view(), name='roles'),
## 视图
class RoleView(APIView):
def post(self, request, *args, **kwargs):
roles_ser = RolesSerializers(data=request.data)
if roles_ser.is_valid():
print(roles_ser.validated_data)
else:
print(roles_ser.errors)
return HttpResponse('OK')
测试: