rest framework 之序列化

Posted midworld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rest framework 之序列化相关的知识,希望对你有一定的参考价值。

一、示例

restful work 的序列号就类似于 Django 的 Form 表单。

1、api/urls.py

from django.urls import path, re_path
from api.views import UserView, ParserView, RolesView

urlpatterns = [
    # path('users/', UserView.as_view()),
    re_path('(?P<version>[v1|v2]+)/users/', UserView.as_view(), name='api_user'),   # 解析

    re_path('(?P<version>[v1|v2]+)/roles', RolesView.as_view()),    # 序列化

    path('parser/', ParserView.as_view(), name='api_parer'),
]
  1. api/views.py
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers
from app import models
import json



class RolesSerializers(serializers.Serializer):
    """序列化类(对应 Role 模型类中字段)"""
    id = serializers.IntegerField()
    name = serializers.CharField()


class RolesView(APIView):
    def get(self, request, *args, **kwargs):
        # 取出全部
        # roles = models.Role.objects.all()
        # # 序列化,两个参数,instance:接受Queryset(或者对象)   mangy=True表示处理多个对象,mant=False表示处理单个对象
        # ser = RolesSerializers(instance=roles, many=True)
        # # 转成json格式,ensure_ascii=False表示显示中文,默认为True
        # ret = json.dumps(ser.data, ensure_ascii=False)

        # 取出第一个
        role = models.Role.objects.all().first()
        ser = RolesSerializers(instance=role, many=False)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)
  1. app/models.py
from django.db import models


class UserInfo(models.Model):
    USER_TYPE = (
        (1, '普通用户'),
        (2, 'VIP'),
        (3, 'SVIP')
    )

    user_type = models.IntegerField(choices=USER_TYPE)
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

    group = models.ForeignKey('UserGroup', on_delete=models.CASCADE, verbose_name='所属组', null=True, blank=True)
    roles = models.ManyToManyField('Role', verbose_name='角色')


class UserToken(models.Model):
    user = models.OneToOneField(UserInfo, on_delete=models.CASCADE)
    token = models.CharField(max_length=64)


class UserGroup(models.Model):
    """分组"""
    name = models.CharField(verbose_name='组名', max_length=64)


class Role(models.Model):
    """角色"""
    name = models.CharField(max_length=64, verbose_name='角色名')
  1. 访问:http://127.0.0.1:8000/api/v1/roles
"id": 1, "name": "老板"

二、进阶

前面我们只序列化了没有关联的字段,对于多选字段、外键以及多对多字段都没有演示,在这里我们还将学习如何自定义一个序列化方法。

1、各个数据表数据:

技术图片

2、api/urls.py

from django.urls import path, re_path
from api.views import UserView, ParserView, RolesView, UserInfoView

urlpatterns = [
    # path('users/', UserView.as_view()),
    re_path('(?P<version>[v1|v2]+)/users/', UserView.as_view(), name='api_user'),

    re_path('(?P<version>[v1|v2]+)/roles', RolesView.as_view()),    # 序列化
    re_path('(?P<version>[v1|v2]+)/info', UserInfoView.as_view()),  # 序列化

    path('parser/', ParserView.as_view(), name='api_parer'),
]

3、api/views.py

class UserInfoSerializers(serializers.Serializer):
    # 多选字段:user_type是choices(1,2,3),显示全称的方法用 get_字段名_display
    type = serializers.CharField(source='get_user_type_display')
    username = serializers.CharField()
    password = serializers.CharField()
    # 外键字段: group.name:组的名字
    group = serializers.CharField(source="group.name")
    # SerializerMethodField(),表示自定义显示
    # 然后写一个自定义的方法
    rls = serializers.SerializerMethodField()

    def get_rls(self, row):
        """获取用户所有角色,get_rls 和上面自定义方法名字一致"""
        print(row)  # UserInfo object (2)
        role_obj_list = row.roles.all()
        print(role_obj_list)    # <QuerySet [<Role: Role object (1)>]>
        ret = []
        # 获取角色的id和名字
        # 以字典的键值对方式显示
        for item in role_obj_list:
            ret.append("id": item.id, "name": item.name)
        return ret


class UserInfoView(APIView):
    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializers(instance=users, many=True)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)
  1. 访问:http://127.0.0.1:8000/api/v1/info
["type": "普通用户", "username": "rose", "password": "123", "group": "一组", "rls": ["id": 1, "name": "老板"], "type": "VIP", "username": "john", "password": "123", "group": "二组", "rls": [], "type": "SVIP", "username": "hj", "password": "123", "group": "三组", "rls": ["id": 4, "name": "普通员工"]]

总结

  • 多选字段:使用 get_字段名_display 获取字段原始值
  • 外键字段:source="外键名.要获取的字段名"
  • 多对多字段:可通过自定义分实现
  • 自定义方法:mth = serializers.SerializerMethodField(),方法名:get_mth()

三、ModelSerializer

ModelSerializer 效果类似于 Django Form 中的 ModelForm

class UserInfoSerializers(serializers.ModelSerializer):
    type = serializers.CharField
    group = serializers.CharField(source="group.name")
    rls = serializers.SerializerMethodField()

    def get_rls(self, row):
       pass

    class Meta:
        model = models.UserInfo
        # fields = '__all__'
        fields = ['id','username','password','type','group','rls']

四、depth 控制连表的深度

当表与表之间多个相连,如果只想显示前面几层,可以使用 depth 控制连表的深度:

class UserInfoSerializers(serializers.ModelSerializer):
    type = serializers.CharField
    group = serializers.CharField(source="group.name")
    rls = serializers.SerializerMethodField()

    def get_rls(self, row):
       pass

    class Meta:
        model = models.UserInfo
        # fields = '__all__'
        fields = ['id','username','password','type','group','rls']
        depth = 1       # 控制为 1 层

六、HyperlinkedidentityField 生成 url

序列化关联数据表的相应字段的 url,就像下面这种:

[
    
        "id": 1,
        "publish": "http://127.0.0.1:8080/api/publish/1",
        "name": "红楼梦",
        "price": "123.00",
        "publish_date": null
    ,
    
        "id": 2,
        "publish": "http://127.0.0.1:8080/api/publish/2",
        "name": "西游记",
        "price": "234.00",
        "publish_date": null
    
]

1、api/serializers.py

from rest_framework import serializers
from app import models


class UserInfoSerializers(serializers.ModelSerializer):
    """用户个人信息序列化"""
    group = serializers.HyperlinkedIdentityField(view_name='gob', lookup_field='group_id', lookup_url_kwarg='pk')

    class Meta:
        model = models.UserInfo
        fields = '__all__'


class RolesSerializers(serializers.Serializer):
    """序列化类"""
    id = serializers.IntegerField()
    name = serializers.CharField()


class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserGroup
        fields = "__all__"

参数:

  • view_name:关联路由 URL 中的 name 参数值,用来反向解析 URL
  • lookup_field:要查找的目标字段,如:group_idUserInfo 模型中的外键字段(即我们要查找的字段)
  • lookup_url_kwarg:关键字参数的名称,该参数对应于查找字段,一般为 pk

2、api/urls.py

from django.urls import path, re_path
from api.views import UserView, ParserView, RolesView, UserInfoView, GroupView

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/info/', UserInfoView.as_view()),  # 序列化

    re_path('(?P<version>[v1|v2]+)/group/(?P<pk>\d+)/', GroupView.as_view(), name='gob'),  # 序列化生成url

]

3、api/views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser, FormParser

from .serializers import GroupSerializer, RolesSerializers, UserInfoSerializers

from app import models
from django.http import JsonResponse


class UserInfoView(APIView):
    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        # 这里必须指定 context,将 request 传递给 UserInfoSerializers
        user_ser = UserInfoSerializers(instance=users, many=True, context='request': request)
        return JsonResponse(user_ser.data, safe=False)


class GroupView(APIView):
    def get(self, request, *args, **kwargs):
        # pk = kwargs.get('pk')
        # group = models.UserGroup.objects.filter(pk=pk).first()
        #
        # group_ser = GroupSerializer(instance=group, many=False)
        # # ret = json.dumps(ser.data, ensure_ascii=False)
        # return JsonResponse(group_ser.data, safe=False)
        return HttpResponse('ok')

4、访问:http://127.0.0.1:8000/api/v2/info/

[
    
        "id": 1,
        'username': 'rose', john/hj
        'password': '123',
        "group": "http://127.0.0.1:8080/api/group/1",
        "roles": [
            2,
        ]
    ,
    
        "id": 2,
        'username': 'john', 
        'password': '123',
        "group": "http://127.0.0.1:8080/api/group/2",
        "roles": [
            1,
        ]
    ,
]

七、用户请求数据验证

验证用户请求的数据是否合法

1、api/serializers.py

from rest_framework import serializers


class UserGroupSerializers(serializers.Serializer):
    """用户请求验证"""
    title = serializers.CharField()

2、api/views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView


from .serializers import GroupSerializer, RolesSerializers, UserInfoSerializers, UserGroupSerializers

class UserGroupView(APIView):
    """用户请求验证"""
    def post(self, request, *args, **kwargs):
        usergroup_ser = UserGroupSerializers(data=request.data)
        if usergroup_ser.is_valid():
            print(usergroup_ser.validated_data['title'])
        else:
            print(usergroup_ser.errors)
        return HttpResponse('用户提交数据错误')

3、利用 postman 模拟用户请求发送数据

请求方式 post,参数格式 JSON:

技术图片

当请求的数据为空时,会自动验证数据的合法性:

技术图片

八、自定义验证规则

你可以自定义验证规则,比如验证用户输入的字段必须以什么开头,必须是邮箱格式等:

1、api/serializers.py

class GroupValidation:
    """自定义验证规则"""
    def __init__(self, base):
        self.base = base    # love

    def __call__(self, value, *args, **kwargs):
        if not value.startswith(self.base):
            msg = '标题必须以 %s 开头' % self.base
            raise serializers.ValidationError(msg)


class UserGroupSerializers(serializers.Serializer):
    """用户请求数据验证"""
    # title = serializers.CharField()
    title = serializers.CharField(validators=[GroupValidation('love'), ])

2、其余的不变,现在验证规则按照我们设置的走,错误提示也一样:

技术图片

以上是关于rest framework 之序列化的主要内容,如果未能解决你的问题,请参考以下文章

Django REST framework之序列化组件以及源码分析+全局局部Hook

Django REST framework框架之GET, POST, PUT, PATCH, DELETE等API请求接口设计

django之序列化数据

django之序列化数据

Django:rest framework之分页(Pagination)

django-rest-framework:如何序列化已经包含 JSON 的字段?