Django Rest framework 之 序列化

▼魔方 西西 提交于 2021-01-23 04:53:12

一、前言

先建立数据库,并添加相应的数据,用来后面序列化使用

1、建立数据库模型

为数据建立相应的数据库模型,并且有一对一,多对多,外键关联。

from django.db import models

class UserGroup(models.Model):
    title = models.CharField(max_length=32)


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

    username = models.CharField(max_length=32,unique=True)
    password = models.CharField(max_length=64)

    group = models.ForeignKey("UserGroup", on_delete=models.CASCADE)
    roles = models.ManyToManyField("Role") 


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


class Role(models.Model):
    title = models.CharField(max_length=32)

并执行数据库迁移操作

python manage.py makemigrations
python manage.py migrate

2、添加少量数据

当数据迁移执行之后,会在sqlite数据库中生活如下表

多对多关系的时候,<code>django</code>自动生成第三张表维系表关系,字段分别是<code>userinfo</code>和<code>role</code>的<code>id</code>,其中<code>api_userinfo_roles</code>为多对多关系生成的表。 在表中添加少量数据

<1>、UserInfo表

<2>、UserGroup表

3、UserInfo_roles表

4、roles表

二、序列化的简单使用

1、不使用序列化

<1>、路由

from django.conf.urls import url

from .views import RoleView

urlpatterns = [
    url(r'^role/$', RoleView.as_view()),
]

<2>、视图

from rest_framework.views import APIView

from .models import Role

import json 

class RoleView(APIView):

    def get(self, request, *args, **kwargs):

        roles = Role.objects.all().values('id' ,'title')
        print(roles, type(roles))  # roles为一个QuerySet对象
        ret_roles = json.dumps(list(roles), ensure_ascii=False)  # 多条数据
        return HttpResponse(ret_roles)

2、简单使用Serializer

<1>、定义序列化类

from rest_framework import serializers

class RoleSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()

<2>、视图

class RoleView(APIView):

    def get(self, request, *args, **kwargs):

        # 多条数据
        # 将序列化后的数据都存到ser.data(OrderDict有序字典中)中
        # roles = Role.objects.all()
        # ser_roles = RoleSerializer(instance=roles, many=True)
        # print(ser_roles, type(ser_roles))  # ListSerializer对象
        # ret_roles = json.dumps(ser_roles.data, ensure_ascii=False)  # 多条数据
        # return HttpResponse(ret_roles)

        # 单条数据
        role = Role.objects.all().first()
        ser_role = RoleSerializer(instance=role, many=False)  # RoleSerializer对象
        print(ser_role, type(ser_role))  # 单条数据
        ret_roles = json.dumps(ser_role.data, ensure_ascii=False)
        return HttpResponse(ret_roles)

总结:上面可以实现数据的简单序列化,但是无法自定义字段,也无法对数据进行处理,不方便,限制较大

三、进一步使用Serializer

1、路由

from django.conf.urls import url

from .views import UserInfo

urlpatterns = [
    url(r'^userinfo/$', UserInfo.as_view()),
]

2、视图

class UserInfoView(APIView):

    def get(self, request, *args, **kwargs):

        users = UserInfo.objects.all()
        users_ser = UserSerializer(instance=users, many=True)
        users_ret = json.dumps(users_ser.data, ensure_ascii=False)
        # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
        return HttpResponse(users_ret)

3、使用serializer

class UserSerializer(serializers.Serializer):
    type = serializers.IntegerField(source='user_type')
    user_type = serializers.CharField(source='get_user_type_display')  # choices字段显示
    username = serializers.CharField()
    pwd = serializers.CharField(source='password')  # 自定义serializer中的key值
    group_title = serializers.CharField(source='group.title')  # 关联对象属性
    roles = serializers.CharField(source='roles.all')  # 多对多关系
    roles_info = serializers.SerializerMethodField()   # 表示自定义方法,显示querytset对象详情

    def get_roles_info(self, row):
        roles = row.roles.all()
        ret = []
        for item in roles:
            ret.append(
                {
                    'id': item.id,
                    'title': item.title
                }
            )
        return ret

  • 如果没有指定在<code>Filed</code>中没有定义<code>source</code>参数的时候,就自动与数据库<code>modles</code>定义的字段进行匹配,如上面的<code>userrname</code>字段。在定义字段后,<code>Serializer</code>类中可以自定义属性如type
  • 当<code>models</code>中是以<code>choice</code>定义时:需要定义<code>source</code>参数定义<code>get_字段名_display</code>才能获取数据,这与在模板语言中的用法一样,如上面的<code>user_type</code>
  • 外键关联的时候,直接 外键字段名.属性 的方式定义传参给source参数即可,如上面的<code>group.title</code>
  • 对于roles字段,想直接获取所有的对象,但是无法做到细粒度的将对象的所有属性展示出来,只能获取到<code>QuerySet</code>对象
  • 自定义字段,处理数据,如<code>roles_info</code>获取所有的<code>role</code>对象的属性,处理数据可以定义方法,方法名格式为<code>get_属性</code>,并<code>return</code>值最终返回值

执行结果:

:自定义字段也可以采取继承的方式,如:

class UsernameField(serializers.CharField):
    def to_representation(self, value):
        return 'username' + value

重写<code>to_representation</code>方法,<code>value</code>为从数据库取出的值,然后对<code>value</code>进行处理,在返回即可 并将序列化类中的username改为 <code>username = UsernameField()</code>

四、使用ModelSerializer组件

1、包装Serializer


class UserSerializer(serializers.ModelSerializer):
    user_type = serializers.CharField(source='get_user_type_display')
    roles = serializers.CharField(source='roles.all')  # 外键关联
    roles_info = serializers.SerializerMethodField()   # 表示自定义方法,显示外键关联详情
    group_title = serializers.CharField(source='group.title')
    def get_roles_info(self, row):
        roles = row.roles.all()
        ret = []
        for item in roles:
            ret.append(
                {
                    'id': item.id,
                    'title': item.title
                }
            )
        return ret

    class Meta:
        model = UserInfo
        # fields = '__all__'  # 为全部的字段做匹配
        fields = ['user_type', 'username', 'password', 'group', 'group_title', 'roles', 'roles_info']  # 自定义需要展示的字段
        extra_kwargs = {'group': {'source': 'group_id'}}

<code>ModelSerializer</code>与<code>Serializer</code>区别在于:<code>ModelSerializer</code>支持了<code>Serializer</code>中所有的操作,并且通过自动生成所有数据字段与序列化类的一一对应关系,而不用自己手动添加。 即<code>Serializer</code>是<code>ModelSerializer</code>的父类,所以<code>ModelSerializer</code>才会支持<code>Serializer</code>的所有操作

返回结果

2、ModelSerializer深度控制

在上面,看到在进行连表查询的时候,只能获取到外键关联对象,在当前表中存储的id,怎样拿到外键关联对象的具体信息。

class UserSerializer(serializers.ModelSerializer):
    # 自动向内部进行深度查询  depth表示查询层数
    class Meta:
        model = UserInfo
        # fields = "__all__"
        fields = ['id','username','password','group','roles']
        depth = 1 # 0 ~ 10  默认的depth为0

class UserInfoView(APIView):

    def get(self, request, *args, **kwargs):

        users = UserInfo.objects.all()
        users_ser = UserSerializer(instance=users, many=True)
        users_ret = json.dumps(users_ser.data, ensure_ascii=False)
        # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
        return HttpResponse(users_ret)

:这里的depth就表示深度查询的层数,默认的层数为0,层数越多查询效率越慢。 返回结果

3、自动生成链接

在上面我们看到,在返回组group的时候是返回该组的<code>id</code>,或者用<code>depth</code>深度控制,返回组的详细信息。在restful规范中,规定应该给出相应的详情链接,可以通过url拼接,在django rest framework中也有相对应的实现。 首先改写一下用户信息序列化类,使之能够提供用户组详情的有关url

class UserSerializer(serializers.ModelSerializer):
    group = serializers.HyperlinkedIdentityField(view_name='api:gp', lookup_field='group_id', lookup_url_kwarg='xxx')  
    # view_name参数 进行传参的时候是参考路由匹配中的name与namespace参数
    #  lookeup_field参数是根据在UserInfo表中的连表查询字段group_id
    # look_url_kwarg参数在做url反向解析的时候会用到

    class Meta:
        model = UserInfo
        fields = ['id','username','password','group','roles']
        depth = 1 # 0 ~ 10


class UserInfoView(APIView):

    def get(self, request, *args, **kwargs):

        users = UserInfo.objects.all()
        users_ser = UserSerializer(instance=users, many=True, context={'request': request})  # 在做链接的时候需要添加context参数
        users_ret = json.dumps(users_ser.data, ensure_ascii=False)
        # print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
        return HttpResponse(users_ret)


# 添加group序列化类
class GroupSerializer(serializers.ModelSerializer):

    class Meta:
        model = UserGroup
        fields = "__all__"

# 返回用户组的详细信息
class GroupView(APIView):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('xxx')
        obj = UserGroup.objects.filter(pk=pk).first()

        ser = GroupSerializer(instance=obj,many=False)
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

返回结果

当我们点解用户组详情链接后,返回结果

4、校验数据

序列化不仅可以做数据的返回,也可以对前端提交的数据进行校验。

<1>、类方法检验

class TitleValidator(object):
    def __init__(self, base):
        self.base = base

    def __call__(self, value):
        if not value.startswith(self.base):
            message = '标题必须以 %s 为开头。' % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        # 执行验证之前调用,serializer_fields是当前字段对象
        pass


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[TitleValidator('Django'),])


class UserGroupView(APIView):

    def post(self,request,*args,**kwargs):

        print(request.data)
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)

        return HttpResponse('提交数据')

上面的<code>TitileValidator</code>类封装了对<code>request.data</code>前端传来的数据的校验,<code>title</code>相对应的是数据中的<code>key</code>为<code>title</code>的值。<code>TitileValidator</code>实现了<code>call()</code>特殊方法,并把具体的验证逻辑封装到里边,是一个可直接调用的对象。而<code>self.base</code>则为具体的<code>title</code>对应的数据,进行处理。

<2>、钩子方法

class UserGroupSerializer(serializers.Serializer):

    title = serializers.CharField()

    def validate_title(self, value):
        from rest_framework import exceptions
        if not value:
           raise exceptions.ValidationError('不可为空')
        return value

class UserGroupView(APIView):

    def post(self,request,*args,**kwargs):

        print(request.data)
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)

        return HttpResponse('提交数据')

在定义钩子方法的时候,钩子函数是以<code>validate_字段名</code>的方式进行命名的。只有遵循这样的格式,在<code>Serializer</code>内部会对钩子函数的名字进行拆分并识别出来。在<code>validate_title</code>内部封装了对数据的校验操作,<code>value</code>则为具体的值

原文出处:https://www.cnblogs.com/welan/p/10151714.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!