前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >5.寻光集后台管理系统-用户管理(序列化器)

5.寻光集后台管理系统-用户管理(序列化器)

作者头像
zx钟
发布2022-12-02 15:40:02
3240
发布2022-12-02 15:40:02
举报
文章被收录于专栏:测试游记测试游记

序列化器

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

也是表示这个字段是个字符串

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中自带了一个判重的校验,如果有定制的校验的话就需要编写一个函数

from rest_framework.validators import UniqueValidator

...
'validators': [UniqueValidator(queryset=User.objects.all(), message='此邮箱已注册')]
...

校验某个字段也可以直接使用validate_字段名来进行校验

比如校验两次输入的密码是否一致:

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)

整体代码

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

请求参数:

{"username":"admin","password":"21232f297a57a5a743894a0e4a801fc3"}

响应参数:

{
    "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方法)中,可以对数据进行一定程度上的改造

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 测试游记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 序列化器
    • 序列化
      • 反序列化
        • 小结
        • 注册序列化器
          • 整体代码
          • 登录序列化器
          • 用户增删改查序列化器
          • 小结
          相关产品与服务
          文件存储
          文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档