Django rest framework(6)----序列化(2)

爲何要序列化

django 查詢數據庫返回的類型是  queryset 類型 而咱們和前端通訊使用的大多數是json類型,這個時候咱們須要把 queryset的數據類型轉換成python的數據類型而後在轉換成json 的格式返回前端

在咱們沒使用  restframework 中的 序列化以前咱們是這樣作的python

class RolesView(APIView):
    def get(self,request,*args,**kwargs):
        roles = models.Role.objects.all().values('id','title')
        roles = list(roles)
        ret = json.dumps(roles,ensure_ascii=False) 
        return HttpResponse(ret)        

 

 json.dumps 中的參數   ensure_ascii=False 意思是不把中文進行轉義,即顯示中文數據庫

restframework 中的序列化器就能夠自動的幫助咱們 把queryset 的數據類型轉化成 python的數據類型django

restframework 序列化的使用

建表  models.py 

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")
    roles = models.ManyToManyField("Role")


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


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

 添加Rolejson

 

查詢全部的角色,使用  restframework進行序列化基本的使用以下後端

  1 寫一個序列化的類繼承  serializers 或  ModelSerializer ,類屬性是要序列化的數據庫表中的字段(注意:這個要對應)app

   2 把建立序列化對象Obj,把須要序列化的數據傳入進去,若是有多條數據 many = True 不然爲False函數

   3   經過json.dumps(Obj.data) 轉化成json格式post

views.py 代碼以下測試

import json
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from . import models
from rest_framework import serializers

#要先寫一個序列化的類
class RolesSerializer(serializers.Serializer):
    #Role表裏面的字段id和title序列化
    id = serializers.IntegerField()
    title = serializers.CharField()

class RolesView(APIView):
    def get(self,request,*args,**kwargs):
     # 方式一 多條數據的狀況:
        # roles = models.Role.objects.all().values('id','title')
        # roles = list(roles)
        # ret = json.dumps(roles,ensure_ascii=False)    
        # 方式二  只有一條數據的狀況
     roles = models.Role.objects.all()
        # 序列化,兩個參數,instance:接受Queryset(或者對象)   mangy=True表示對Queryset進行處理,mant=False表示對對象進行進行處理
        ser = RolesSerializer(instance=roles,many=True)
        # 轉成json格式,ensure_ascii=False表示顯示中文,默認爲True
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)   

 

添加對應路由

urlpatterns = [
    url(r'roles/$', views.RolesView.as_view()),
]

 

測試的結果以下:

 進階使用

 上述的狀況是普通的字段,若是是特殊的字段,那麼咱們又該如何去處理呢,咱們來操做user表,表中的字段以下

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")
    roles = models.ManyToManyField("Role")

  

咱們使用上述的方法對其全部的字段進行序列化代碼以下

class UserInfoSerializer(serializers.Serializer):
    user_type = serializers.CharField() # row.user_type
    username = serializers.CharField()
    password = serializers.CharField()
    group = serializers.CharField()
    roles = serializers.CharField()

class UserInfoView(APIView):
    def get(self,request,*args,**kwargs):

        users = models.UserInfo.objects.all()

        # 對象, Serializer類處理; self.to_representation
        # QuerySet,ListSerializer類處理; self.to_representation
        ser = UserInfoSerializer(instance=users,many=True)
        ret = json.dumps(ser.data, ensure_ascii=False)
        return HttpResponse(ret)

 

爲其添加路由

urlpatterns = [
    url('(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(),name='uuu'),
    url(r'parser/$', views.ParserView.as_view()),
    url(r'roles/$', views.RolesView.as_view()),
    url(r'userinfo/$', views.UserInfoView.as_view()),
]

 

測試的結果以下

狀況一  對於數據庫中自已選擇的字段

   對於表中的 user_type是choices(1,2,3)咱們想顯示全稱 ,解決的方法是 傳入參數source="get_user_type_display" ,它會自動的幫咱們執行 get_user_type_display方法

修改後的序列化器以下

class UserInfoSerializer(serializers.Serializer):
    # user_type = serializers.CharField() # row.user_type
    user_type = serializers.CharField(source="get_user_type_display") # row.get_user_type_display()

    username = serializers.CharField()
    password = serializers.CharField()
    group = serializers.CharField()
    roles = serializers.CharField()

 

 測試的結果以下

 

狀況二對於外鍵(一對一)

   對於表中外鍵的字段gruop ,咱們想顯示與其關聯的title字段的內容  ,解決的方法是仍是經過傳入參數source="group.title"  它會自動的去查找

修改後的序列化器以下

 狀況三 對於外鍵(多對多的狀況)

   對於外鍵多對多的狀況咱們想顯示其中的一些字段,這個時候因爲是多對多它是一個可迭代的對象,因此不能直接向上面一對一的狀況直接取,解決的方法這時候咱們須要自定義方法來獲取

特別須要註音的是

  1 若是  要顯示外鍵多對多內容 要使用  roles = serializers.SerializerMethodField()  

   2  還有就是自定義函數的函數名應該是  若是多對多的外鍵是roles 函數名則應該是 get_roles(self,role--->能夠隨命名)  ,若是是rls則函數名爲get_rls

代碼以下

class UserInfoSerializer(serializers.Serializer):
    # user_type = serializers.CharField() # row.user_type
    user_type = serializers.CharField(source="get_user_type_display") # row.get_user_type_display()

    username = serializers.CharField()
    password = serializers.CharField()
    # group = serializers.CharField()
    group = serializers.CharField(source='group.title')
    # roles = serializers.SerializerMethodField()
    roles = serializers.SerializerMethodField()  # 自定義顯示
    def get_roles(self,row):

        role_obj_list = row.roles.all()

        ret = []
        for item in role_obj_list:
            ret.append({'id':item.id,'title':item.title})
        return ret

 

 測試的結果以下

 ModelSerializer 序列化器的使用

 ModelSerializer 繼承 的是Serializer ,因此咱們在使用它的時候一樣能夠混合着 Serializer 使用,它主要是在序列化字段的時候作了一些簡化能夠簡單的對字段進行批量的定義和所有序列

在用的時候須要注意的是  對於一些普通的字段不能調用的應該放入 列表中,要是向Serializer 中  password = serializers.CharField() 這樣去使用會報錯

基本使用

class UserInfoSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.UserInfo
        fields = "__all__"  # 序列話全部的字段
        # fields = ['id','username','password','oooo','rls','group','x1']  # 對指定的字段進行批量處理

 

測試的結果以下

和Serializer混合使用

class UserInfoSerializer(serializers.ModelSerializer):
    oooo = serializers.CharField(source="get_user_type_display")  # row.user_type
    rls = serializers.SerializerMethodField()  # 自定義顯示


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

    def get_rls(self, row):
        role_obj_list = row.roles.all()

        ret = []
        for item in role_obj_list:
            ret.append({'id':item.id,'title':item.title})
        return ret

 

測試結果以下

自動序列化連表(depth)

 在使用 ModelSerializer 遇到連表的操做,須要咱們結合 Serializer 混合使用,其實在ModelSerializer 中還有一個參數表示depth表示連表的深度能夠自動的幫咱們去取數據

 depth 表示誇幾張表查詢數據,官方建議的參數是 ( 0 ~ 10 )

修改後的序列化器以下所示:

class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        #fields = "__all__"
        fields = ['id','username','password','group','roles']
        #表示連表的深度
        depth = 1


class UserInfoView(APIView):
    '''用戶的信息'''
    def get(self,request,*args,**kwargs):
        users = models.UserInfo.objects.all()
        ser = UserInfoSerializer(instance=users,many=True)
        ret = json.dumps(ser.data,ensure_ascii=False)
        return HttpResponse(ret)

 

測試的結果以下

 

生成URL

有時候一些數據,咱們須要生成url返回給用戶,讓用戶再次請求獲取想要的數據

 基本的使用以下:

  添加字段   group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx')     

  參數說明   view_name 是 須要生成的url 中的name    lookup_field 須要生成url的真實傳入到額參數,  lookup_url_kwarg 須要生成url的形參

修改後的序列化器以下所示

class UserInfoSerializer(serializers.ModelSerializer):
    group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx')
    class Meta:
        model = models.UserInfo
        # fields = "__all__"
        fields = ['id','username','password','group','roles']
        depth = 0 # 0 ~ 10

 

添加再次請求的類視圖和序列化器

class GroupSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.UserGroup
        fields = "__all__"

class GroupView(APIView):
    def get(self,request,*args,**kwargs):
        pk = kwargs.get('xxx')
        obj = models.UserGroup.objects.filter(pk=pk).first()

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

 

添加路由

urlpatterns = [
    url('(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(),name='uuu'),
    url(r'parser/$', views.ParserView.as_view()),
    url(r'roles/$', views.RolesView.as_view()),
    url(r'userinfo/$', views.UserInfoView.as_view()),
    url(r'^(?P<version>[v1|v2]+)/group/(?P<xxx>\d+)$', views.GroupView.as_view(),name='gp'),
]

 

測試的結果以下

 

用戶請求數據驗證

 使用序列化器對用戶傳送的值進行非空的判斷

 

添加類視圖代碼以下

class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={'required':'標題不能爲空'})


class UserGroupView(APIView):

    def post(self,request,*args,**kwargs):
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)

        return HttpResponse('提交數據')

 

添加路由 

url(r'usergroup/$', views.UserGroupView.as_view(),name='gp'),

  

測試傳入的值爲空的接口以下

傳入的值爲空,後臺打印的數據以下

測試非空的值

後臺打印的結果以下

自定義驗證規則

自定義了一個規則,傳入的值必須以老男人開頭,不然驗證不經過

 完整的代碼以下

class XXValidator(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):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 執行驗證以前調用,serializer_fields是當前字段對象
        pass

class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(error_messages={'required':'標題不能爲空'},validators=[XXValidator('老男人'),])



class UserGroupView(APIView):

    def post(self,request,*args,**kwargs):
        ser = UserGroupSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data['title'])
        else:
            print(ser.errors)

        return HttpResponse('提交數據')

 

測試知足自定義規則的數據的結果以下

後臺打印數據以下

測試不知足自定義規則

 

 

 後臺打印結果以下

 

write_only和write_only的區別

  write_only  包含這個字段只做用於反序 (即對前端傳來的值作序列話),正序的時候會忽略該字段

  read_only  包含這個字段只做用於正序 (即查詢數據庫作序列化返回給前端),反序的時候會忽略該字段

方法字段

serializers.SerializerMethodField 會把一個字段變成一個方法的字段,因此要爲這個字段定義一個方法,返回的值爲做爲該字段的值

實例以下

表結構

class Book(models.Model):
    title = models.CharField(max_length=32)
    CHOICES = ((1, "Python"), (2, "Linux"), (3, "go"))
    category = models.IntegerField(choices=CHOICES)
    pub_time = models.DateField()
    publisher = models.ForeignKey(to="Publisher")
    authors = models.ManyToManyField(to="Author")
    
    
class Publisher(models.Model):
    title = models.CharField(max_length=32)
    

class Author(models.Model):
    name = models.CharField(max_length=32)

序列化

class BookSerializer(serializers.ModelSerializer):


    publisher_info = serializers.SerializerMethodField(read_only=True)
    authors_info = serializers.SerializerMethodField(read_only=True)
    
    def get_authors_info(self, obj):
        authors_querset = obj.authors.all()
        return [{"id": author.id, "name": author.name} for author in authors_querset]
    
    def get_publisher_info(self, obj):
        publisher_obj = obj.publisher
        return {"id": publisher_obj.id, "title": publisher_obj.title}
    
    class Meta:
        model = Book
        fields = "__all__"
        # exclude=["id"]
        # 會讓你這些全部的外鍵關係變成read_only = True
        # depth = 1
        extra_kwargs = {"publisher": {"write_only": True}, "authors":{"write_only": True}}

 

簡單實例 

class BookEditView(APIView):
    def get(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)
    
    def put(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)

  

ModelSerializer 中 deep 使用注意事項

在使用  deep  是 會把全部的 外鍵關係變成  read_only = True ,解決這個問題,咱們能夠 經過   extra_kwargs 爲其添加額外的參數配置 

 

    class Meta:
        model = Book
        fields = "__all__"
        # exclude=["id"]
        # 會讓你這些全部的外鍵關係變成read_only = True
        depth = 1
        extra_kwargs = {"publisher": {"write_only": True}, "authors":{"write_only": True}}

驗證

-- 單個字段的驗證  權重 222
    def validate_字段名稱(self, value):
        不經過 raise serializers.ValidationError("錯誤信息")
        經過 return value
-- 多個字段的驗證  權重 333
    def validate(self, attrs):
        attrs 是全部字段組成的字典
        不經過 raise serializers.ValidationError("錯誤信息")
        經過 return attrs
-- 自定義的驗證  權重 111
    def my_validate(value):
        不經過 raise serializers.ValidationError("錯誤信息")
        經過 return value
    配置
        -- 給字段加validators=[my_validate]

 

簡單用法  

    def validate_title(self, value):
        print(2222)
        # value就是title的值 對value處理
        if "python" not in value.lower():
            raise serializers.ValidationError("標題必須含有python")
        return value

    def validate(self, attrs):
        print(33333)
        # attrs 字典有你傳過來的全部的字段
        print(attrs)
        if "python" in attrs["title"].lower() or attrs["post_category"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分類或標題不合符要求")

  

重寫update方法

    def update(self, instance, validated_data):
        # instance 更新的book_obj 對象
        # validated_data 校驗經過的數據
        # ORM作更新操做
        instance.title = validated_data.get("title", instance.title)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.category = validated_data.get("post_category", instance.category)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        if validated_data.get("author_list"):
            instance.authors.set(validated_data["author_list"])
        instance.save()
        return instance

 

重寫create方法 

    def create(self, validated_data):
        # validated_data 校驗經過的數據 就是book_obj
        # 經過ORM操做給Book表增長數據
        print(validated_data)
        book_obj = Book.objects.create(title=validated_data["title"], pub_time=validated_data["pub_time"], category=validated_data["post_category"], publisher_id=validated_data["publisher_id"])
        print(book_obj)
        book_obj.authors.add(*validated_data["author_list"])
        return book_obj

  

 過濾Filtering

對於列表數據可能須要根據字段進行過濾,咱們能夠經過添加django-fitlter擴展來加強支持。

pip install django-filter

註冊應用:

INSTALLED_APPS = [
"django_filters",
]

 在配置文件中增長過濾後端的設置:

REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

  

在視圖中添加filter_fields屬性,指定能夠過濾的字段

class BookListView(ListAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
filter_fields = ('btitle', 'bread')
相關文章
相關標籤/搜索