Django Rest Framework(2)-----序列化詳解(serializers)

   REST framework中的序列化類與Django的FormModelForm類很是類似。咱們提供了一個Serializer類,它提供了一種強大的通用方法來控制響應的輸出,以及一個ModelSerializer類,它爲建立處理模型實例和查詢集的序列化提供了有效的快捷方式。git

Serializers 

  序列化器容許把像查詢集和模型實例這樣的複雜數據轉換爲能夠輕鬆渲染成JSONXML或其餘內容類型的原生Python類型。序列化器還提供反序列化,在驗證傳入的數據以後容許解析數據轉換回複雜類型。不只僅有序列化功能,更提供了數據驗證的功能(與django中的form相似)github

 

牛刀小試:

    讓咱們從建立一個簡單的對象開始,咱們可使用下面的例子:django

from datetime import datetime

class Comment(object):
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()

comment = Comment(email='leila@example.com', content='foo bar')

    而後聲明一個序列化器,咱們可使用它來序列化和反序列化與Comment對象相應的數據。json

聲明一個序列化器看起來很是像聲明一個form:api

from rest_framework import serializers

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()
 

一、序列化與反序列化

# 序列化(Python原生的數據類型dict)
serializer = CommentSerializer(comment)
serializer.data   # {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

#
轉化爲json類型 from rest_framework.renderers import JSONRenderer json = JSONRenderer().render(serializer.data) json # b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'


#
反序列化 from django.utils.six import BytesIO from rest_framework.parsers import JSONParser stream = BytesIO(json) data = JSONParser().parse(stream)

二、保存實例

若是咱們但願可以返回基於驗證數據的完整對象實例,咱們須要實現其中一個或所有實現.create()update()方法。例如:ide

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)
        # return Comment.objects.create(**validated_data) # model對象

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        # instance.save() model對象
        return instance

ps:.create().update()方法都是可選的。你能夠根據你序列化器類的用例不實現、實現它們之一或都實現。post

如今當咱們反序列化數據的時候,基於驗證過的數據咱們能夠調用.save()方法返回一個對象實例。網站

comment = serializer.save()

調用.save()方法將建立新實例或者更新現有實例,具體取決於實例化序列化器類的時候是否傳遞了現有實例:ui

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

默認狀況下,序列化程序必須爲全部必填字段傳遞值,不然會引起驗證錯誤。你可使用partial參數以容許部分更新spa

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data,partial=True)

 三、驗證

    反序列化數據的時候,你始終須要先調用is_valid()方法,而後再嘗試去訪問通過驗證的數據或保存對象實例。若是發生任何驗證錯誤,.errors屬性將包含表示生成的錯誤消息的字典。例如:

serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors   # {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}

字典裏的每個鍵都是字段名稱,值是與該字段對應的任何錯誤消息的字符串列表。non_field_errors鍵可能存在,它將列出任何通常驗證錯誤信息。non_field_errors的名稱能夠經過REST framework設置中的NON_FIELD_ERRORS_KEY來自定義。 當對對象列表進行序列化時,返回的錯誤是每一個反序列化項的字典列表。

拋出無效數據的異常

.is_valid()方法使用可選的raise_exception標誌,若是存在驗證錯誤將會拋出一個serializers.ValidationError異常。

這些異常由REST framework提供的默認異常處理程序自動處理,默認狀況下將返回HTTP 400 Bad Request響應。

# 若是數據無效就返回400響應
serializer.is_valid(raise_exception=True)

字段級別的驗證

    你能夠經過向你的Serializer子類中添加.validate_<field_name>方法來指定自定義字段級別的驗證。這些相似於Django表單中的.clean_<field_name>方法。

    這些方法採用單個參數,即須要驗證的字段值。

    你的validate_<field_name>方法應該返回一個驗證過的數據或者拋出一個serializers.ValidationError異常。例如:

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value

注意: 若是你在序列化器中聲明<field_name>的時候帶有required=False參數,字段不被包含的時候這個驗證步驟就不會執行。

對象級別的驗證

    要執行須要訪問多個字段的任何其餘驗證,請添加一個.validate()方法到你的Serializer子類中。這個方法採用字段值字典的單個參數,若是須要應該拋出一個 ValidationError異常,或者知識返回通過驗證的值。例如:

from rest_framework import serializers

class EventSerializer(serializers.Serializer):
    description = serializers.CharField(max_length=100)
    start = serializers.DateTimeField()
    finish = serializers.DateTimeField()

    def validate(self, data):
        """
        Check that the start is before the stop.
        """
        if data['start'] > data['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

驗證器

序列化器上的各個字段均可以包含驗證器,經過在字段實例上聲明,例如:

def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
    score = IntegerField(validators=[multiple_of_ten])

序列化器類還能夠包括應用於一組字段數據的可重用的驗證器。validators給咱們提供了不少很好的功能:UniqueValidator,UniqueTogetherValidator等。

這些驗證器要在內部的Meta類中聲明,以下所示:

UniqueTogetherValidator:(表示聯合惟一)

class EventSerializer(serializers.Serializer):
    name = serializers.CharField()
    room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
    date = serializers.DateField()

    class Meta:
        # 每間屋子天天只能有1個活動。
        validators = UniqueTogetherValidator(
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

UniqueValidator:

username = serializers.CharField(
        max_length=11, 
        min_length=11,
        validators=[UniqueValidator(queryset=UserProfile.objects.all())
    )

更多信息請參閱 validators文檔

 

ModelSerializer

    它爲建立用於處理模型實例和查詢集的序列化程序提供了有用的快捷實現方式。自動建立一個Serializer類,字段與model的字段一一對應。

ModelSerializer類與常規Serializer類相同,不一樣之處在於:

  • 它會根據模型自動生成一組字段。
  • 它會自動爲序列化類生成驗證器,例如unique_together驗證器。
  • 它包含.create()和.update()的簡單默認實現。它可以知足將post或patch上來的數據進行進行直接地建立與更新,除非有額外需求,那麼就能夠重載create與update方法。

聲明ModelSerializer以下,在Meta中設置fields字段,系統會自動進行映射成序列化字段,省去每一個字段再寫一個field。

一、序列化

class CourseSerializer(serializers.ModelSerializer):
    """
    課程序列化
    """class Meta:
        model = models.Course
        # 字段顯示
        fields = ['id','title','level'] # 
        # fields = '__all__':  表示全部字段 
        # exclude = ('add_time',): 除去指定的某些字段
        # 只讀字段  ---多個字段只讀,咱們能夠這樣設置,不用每一個字段都設置read_only=True
        read_only_fields=(「field_name」,) # editable=False、AutoField 默認只讀,不用添加

二、關於外鍵

任何關係(如模型上的外鍵)都將映射到PrimaryKeyRelatedField(但得到外鍵類別的id)。除非在序列化關係文檔中指定,不然默認不包括反向關係。

class CourseDetailSerializer(serializers.ModelSerializer):
    """
    課程詳細序列化
    """
    # choice
    level = serializers.CharField(source='course.get_level_display')
    # one2one/fk
    # 外鍵方式一 須要單個信息
    title = serializers.ReadOnlyField(source='course.title')
    # 外鍵方式二 須要更多信息
    course=CourseSerializer(many=True)

    # m2m 方法一:使用SerializerMethodField(method_name=None)方法,but該方法爲readonly字段。
    #---當不指定method_name ,默認爲get_field_name,
    recommends = serializers.SerializerMethodField()
    chapter = serializers.SerializerMethodField()


    class Meta:
        model = models.CourseDetail
        fields = ['course','title','img','level','slogon','why','recommends','chapter']
        # 外鍵方式三 depth 表示應該遍歷的關聯深度
        # depth = 1  另外訪問時顯示外鍵字段的全部信息,可是隻讀的,不可編輯,即新建時不能傳值
   def get_recommends(self,obj): 

       # 獲取推薦的全部課程 
       queryset = obj.recommend_courses.all() 
       return [{'id':row.id,'title':row.title} for row in queryset] 

   def get_chapter(self,obj): 

      # 獲取推薦的全部課程 
      queryset = obj.course.chapter_set.all() 
      return [{'id':row.id,'name':row.name} for row in queryset]

ModelSerializer主要須要解決的2個問題:

 1,某個字段不屬於指定model,它是write_only,須要用戶傳進來,但咱們不能對它進行save( ),由於ModelSerializer是基於Model,這個字段在Model中沒有對應,這個時候,咱們須要重載validate!
  如在用戶註冊時,咱們須要填寫驗證碼,這個驗證碼只須要驗證,不須要保存到用戶這個Model中:
def validate(self, attrs):
        del attrs["code"]
        return attrs
2,某個字段不屬於指定model,它是read_only,只須要將它序列化傳遞給用戶,可是在這個model中,沒有這個字段!咱們須要用到SerializerMethodField。
  假設須要返回用戶加入這個網站多久了,不可能維持這樣加入的天數這樣一個數據,通常會記錄用戶加入的時間點,而後當用戶獲取這個數據,咱們再計算返回給它。
class UserSerializer(serializers.ModelSerializer):    
    days_since_joined = serializers.SerializerMethodField()
        
    class Meta:
        model = User

    # 方法寫法:get_ + 字段
    def get_days_since_joined(self, obj):
    # obj指這個model的對象
        return (now() - obj.date_joined).days
相關文章
相關標籤/搜索