序列化器
drf的核心概念。
drf提供了一个serializer
类,它可以非常方便的序列化
模型对象和查询集为json
或者其他形式的内容。
还可以提供反序列化
,允许在通过验证传入数据后将解析的数据转换为复杂的类型对象。
序列化
obj->json/html
反序列化
json->obj
小结
- 在
处理请求
到服务器的时候会对数据进行反序列化成python的对象然后再处理 - 在
发送请求
到前端时,会将python对象转化成json发送出去
注册序列化器
在users
路径下新建一个序列化器:
backend/apps/users/serializers.py
在注册账号的时候,需要用户通过前端发送用户名
、密码
、邮箱
、姓名
、电话
这些信息过来,然后后台将他们存到数据库里面
在一些网站注册的时候还会要求用户输入两遍密码,防止用户输入错误,所以我们也做一下这个功能
编写一个类继承于ModelSerializer
from rest_framework import serializers
class UserRegisterSerializer(serializers.ModelSerializer):
新增一个再次输入密码
的字段,写法和model类似,只是变为了serializers.CharField
也是表示这个字段是个字符串
代码语言:javascript复制password_confirm = serializers.CharField(
label='确认密码', help_text='确认密码', write_only=True,
min_length=, max_length=,
error_messages={
'min_length': '仅允许6-20个字符的密码',
'max_length': '仅允许6-20个字符的密码',
})
write_only=True:表示只有在进行写入操作的时候才会用到这个字段
就是说只有前端往后端发送请求的时候(写)才需要这个字段
再写一个Meta
类,标注使用的数据库,需要的字段
class Meta:
model = User
fields = ('id', 'username', 'password', 'password_confirm', 'email', 'mobile', 'name')
extra_kwargs = {
'username': {
'label': '用户名',
'help_text': '用户名',
'min_length': ,
'max_length': ,
'error_messages': {
'min_length': '仅允许6-20个字符的用户名',
'max_length': '仅允许6-20个字符的用户名',
},
},
'password': {
'label': '密码',
'help_text': '密码',
'min_length': ,
'max_length': ,
'write_only': True,
'error_messages': {
'min_length': '仅允许6-20个字符的密码',
'max_length': '仅允许6-20个字符的密码',
},
},
'email': {
'label': '邮箱',
'help_text': '邮箱',
'required': True,
'allow_blank': True,
'validators': [UniqueValidator(queryset=User.objects.all(), message='此邮箱已注册')]
},
}
drf中自带了一个判重的校验,如果有定制的校验的话就需要编写一个函数
代码语言:javascript复制from rest_framework.validators import UniqueValidator
...
'validators': [UniqueValidator(queryset=User.objects.all(), message='此邮箱已注册')]
...
校验某个字段也可以直接使用validate_字段名
来进行校验
比如校验两次输入的密码是否一致:
代码语言:javascript复制def validate_password_confirm(self, value):
"""
校验两次输入的密码是否一致
:param value:
:return:
"""
if value != self.initial_data['password']:
raise serializers.ValidationError('两次输入的密码不一致!')
return value
注册账号,其实就是往用户表中插入一条用户信息,但是我们用户表中其实是没有password_confirm
字段的,所以注册的时候需要剔除它
所以需要重写下create
方法
def create(self, validated_data):
validated_data.pop('password_confirm')
return User.objects.create_user(**validated_data)
整体代码
代码语言:javascript复制class UserRegisterSerializer(serializers.ModelSerializer):
# password_confirm设置 write_only 只有注册的时候需要使用,返回的时候不需要
password_confirm = serializers.CharField(label='确认密码', help_text='确认密码', write_only=True,
min_length=, max_length=,
error_messages={
'min_length': '仅允许6-20个字符的密码',
'max_length': '仅允许6-20个字符的密码',
})
class Meta:
model = User
fields = ('id', 'username', 'password', 'password_confirm', 'email', 'mobile', 'name')
# model指定的模型中没有的字段,不能在extra_kwargs中定义
extra_kwargs = {
'username': {
'label': '用户名',
'help_text': '用户名',
'min_length': ,
'max_length': ,
'error_messages': {
'min_length': '仅允许6-20个字符的用户名',
'max_length': '仅允许6-20个字符的用户名',
},
},
'password': {
'label': '密码',
'help_text': '密码',
'min_length': ,
'max_length': ,
'write_only': True,
'error_messages': {
'min_length': '仅允许6-20个字符的密码',
'max_length': '仅允许6-20个字符的密码',
},
},
'email': {
'label': '邮箱',
'help_text': '邮箱',
'required': True,
'allow_blank': True,
'validators': [UniqueValidator(queryset=User.objects.all(), message='此邮箱已注册')]
},
}
def validate_password_confirm(self, value):
"""
校验两次输入的密码是否一致
:param value:
:return:
"""
if value != self.initial_data['password']:
raise serializers.ValidationError('两次输入的密码不一致!')
return value
def create(self, validated_data):
validated_data.pop('password_confirm')
# 注意调用create_user方法来创建用户,会对密码进行加密
return User.objects.create_user(**validated_data)
所以发送注册请求的时候使用UserRegisterSerializer
序列化器来将json数据进行处理和校验,然后写入数据库完成注册
登录序列化器
为了减少前端的修改,我们将后端部分尽量修改的兼容前端处理
运行一下前端服务,查看前端的登录请求
请求地址:http://localhost:2800/api/token
请求方式:POST
请求参数:
代码语言:javascript复制{"username":"admin","password":"21232f297a57a5a743894a0e4a801fc3"}
响应参数:
代码语言:javascript复制{
"code": ,
"data": {
"token": "SCUI.Administrator.Auth",
"userInfo": {
"userId": "1",
"userName": "Administrator",
"dashboard": "0",
"role": [
"SA",
"admin",
"Auditor"
]
}
},
"message": ""
}
所以仿照一下它的响应内容,加上jwt编写一个MyTokenObtainPairSerializer
序列化器
JWT是JSON Web Token的缩写,是为了在网络应用环境间传递声明而执行的- -种基于JSON的开放标准((RFC 7519)。JWT本身没有定义任何技术实现,它只是定义了一种基于Token的会话管理的规则,涵盖Token需要包含的标准内容和Token的生成过程,特别适用于分布式站点的单点登录(SSO) 场景。
在校验(返回)的时候处理下准备返回的数据,把原来的字段access
替换为token
新增userInfo
部分,将对应的用户id
、姓名
、权限
返回
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
data['token'] = data.pop('access')
data["userInfo"] = {
"userId": self.user.id,
"userName": self.user.name or "匿名",
"dashboard": "0",
"is_superuser": self.user.is_superuser,
"role": self.user.roles and self.user.roles.split(",") or []
}
return data
因为权限存的时候是存的字符串,所以返回的时候为了和前端一致,使用split
进行分割
self.user.roles and self.user.roles.split(",") or []
用户增删改查序列化器
在需求中,管理员需要能对其他账号进行增删改查操作,所以需要编写一个比较全的序列化器来处理
密码
在响应的时候不展示,所以在extra_kwargs
中特别标注更改密码
的时候由于密码是加密的,所以需要使用自带的set_password
方法来进行处理创建账号
由于这个是用于管理员的创建账号,所以就不用再次输入密码了,因为就算密码写错了,管理员也可以很方便的直接修改密码
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password', 'mobile', 'email', 'is_staff', 'is_active', 'is_superuser', 'name',
'date_joined', 'roles']
extra_kwargs = {
'password': {
'write_only': True # 展示的时候不渲染密码
}
}
def create(self, validated_data):
user = super().create(validated_data)
# 手动的处理密码
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
obj = super().update(instance, validated_data)
# 判断是否有password过来
password = validated_data.get('password')
if password is not None:
obj.set_password(password)
obj.save()
return obj
小结
序列化器是DRF框架最关键的一部分,用好序列化器可以大大减少不必要的方法重写
建议所有的请求和响应都要经过序列化器,也就是
- 想要返回一个json,先写一个对应数据结构的序列化器
- 想要处理一个json,先写一个对应数据结构的序列化器
在序列化器的校验(validate
方法)中,可以对数据进行一定程度上的改造