rest framework框架--序列化

数据准备

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]之间。

序列化源码分析

  1. 创建继承了ModelSerializer的类
  2. 实例化类users_ser = UserInfoSerializers(instance=users, many=True)
  3. 实例化类的时候会先调用__new__,然后调用__init__方法
    3.1 BaseSerializer__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')

测试:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值