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


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

Serializers 

  序列化器允許把像查詢集和模型實例這樣的復雜數據轉換為可以輕松渲染成JSONXML或其他內容類型的原生Python類型。序列化器還提供反序列化,在驗證傳入的數據之后允許解析數據轉換回復雜類型。不僅僅有序列化功能,更提供了數據驗證的功能(與django中的form類似)

 

牛刀小試:

    讓我們從創建一個簡單的對象開始,我們可以使用下面的例子:

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對象相應的數據。

聲明一個序列化器看起來非常像聲明一個form:

from rest_framework import serializers

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

1、序列化與反序列化

# 序列化(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)

2、保存實例

如果我們希望能夠返回基於驗證數據的完整對象實例,我們需要實現其中一個或全部實現.create()update()方法。例如:

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()方法都是可選的。你可以根據你序列化器類的用例不實現、實現它們之一或都實現。

現在當我們反序列化數據的時候,基於驗證過的數據我們可以調用.save()方法返回一個對象實例。

comment = serializer.save()

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

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

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

默認情況下,序列化程序必須為所有必填字段傳遞值,否則會引發驗證錯誤。你可以使用partial參數以允許部分更新

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

 3、驗證

    反序列化數據的時候,你始終需要先調用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。

1、序列化

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 默認只讀,不用添加

2、關於外鍵

任何關系(如模型上的外鍵)都將映射到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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM