Django Rest framework 之 序列化

1、前言

先創建數據庫,並添加相應的數據,用來後面序列化使用html

一、創建數據庫模型

爲數據創建相應的數據庫模型,而且有一對一,多對多,外鍵關聯。前端

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

python manage.py makemigrations
python manage.py migrate

二、添加少許數據

當數據遷移執行以後,會在sqlite數據庫中生活以下表
sql

多對多關係的時候,django自動生成第三張表維繫表關係,字段分別是userinforoleid,其中api_userinfo_roles爲多對多關係生成的表。
在表中添加少許數據數據庫

<1>、UserInfo表

<2>、UserGroup表

三、UserInfo_roles表

四、roles表

2、序列化的簡單使用

一、不使用序列化

<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)

二、簡單使用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)

總結:上面能夠實現數據的簡單序列化,可是沒法自定義字段,也沒法對數據進行處理,不方便,限制較大django

3、進一步使用Serializer

一、路由

from django.conf.urls import url

from .views import UserInfo

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

二、視圖

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)

三、使用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

json

  • 若是沒有指定在Filed中沒有定義source參數的時候,就自動與數據庫modles定義的字段進行匹配,如上面的userrname字段。在定義字段後,Serializer類中能夠自定義屬性如type
  • models中是以choice定義時:須要定義source參數定義get_字段名_display才能獲取數據,這與在模板語言中的用法同樣,如上面的user_type
  • 外鍵關聯的時候,直接 外鍵字段名.屬性 的方式定義傳參給source參數便可,如上面的group.title
  • 對於roles字段,想直接獲取全部的對象,可是沒法作到細粒度的將對象的全部屬性展現出來,只能獲取到QuerySet對象
  • 自定義字段,處理數據,如roles_info獲取全部的role對象的屬性,處理數據能夠定義方法,方法名格式爲get_屬性,並return值最終返回值

執行結果:api

:自定義字段也能夠採起繼承的方式,如:restful

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

重寫to_representation方法,value爲從數據庫取出的值,而後對value進行處理,在返回便可
並將序列化類中的username改成
username = UsernameField()app

4、使用ModelSerializer組件

一、包裝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'}}

ModelSerializerSerializer區別在於ModelSerializer支持了Serializer中全部的操做,而且經過自動生成全部數據字段與序列化類的一一對應關係,而不用本身手動添加。
SerializerModelSerializer的父類,因此ModelSerializer纔會支持Serializer的全部操做

返回結果

二、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,層數越多查詢效率越慢。
返回結果

三、自動生成連接

在上面咱們看到,在返回組group的時候是返回該組的id,或者用depth深度控制,返回組的詳細信息。在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)

返回結果

當咱們點解用戶組詳情連接後,返回結果

四、校驗數據

序列化不只能夠作數據的返回,也能夠對前端提交的數據進行校驗。

<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('提交數據')

上面的TitileValidator類封裝了對request.data前端傳來的數據的校驗,title相對應的是數據中的keytitle的值。TitileValidator實現了call()特殊方法,並把具體的驗證邏輯封裝到裏邊,是一個可直接調用的對象。而self.base則爲具體的title對應的數據,進行處理。

<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('提交數據')

在定義鉤子方法的時候,鉤子函數是以validate_字段名的方式進行命名的。只有遵循這樣的格式,在Serializer內部會對鉤子函數的名字進行拆分並識別出來。在validate_title內部封裝了對數據的校驗操做,value則爲具體的值

相關文章
相關標籤/搜索