Django REST Framework API Guide 04

本節大綱linux

  一、serializers數據庫

 

 

 

一、Serializers

Serializers容許複雜的數據,像queryset和模型實例轉換成源生的Python數據類型。從而能夠更簡單的被渲染成JSON,XML或其餘內容類型。Serializers也提供了反序列化的功能,容許解析過的數據轉化爲複雜的類型,在即將到來的數據被驗證完以後。django

另外的一點就是REST framework跟Django的Form和ModelForm類很類似。這裏也提供了2個類,SerializerModelSerializer這兩個功能強大的類,用來建立序列化,處理模型實例和queryset。json

 

Declaring Serializersapi

一開始,讓咱們建立一個簡單對象做爲例子。app

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

咱們將申明一個serializer,它能夠用來序列化和反序列化和Comment對象對應的數據。less

申明一個serializer看起來跟申明一個form很像。ide

from rest_framework import serializers

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

 

Serializing objectspost

如今使用CommentSerializer來序列化comment或者comment列表。使用Serializer類,跟使用Form類同樣。ui

serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

此時,咱們已經轉換模型實例到Python的源生數據類型。在序列化進程的最後,將數據渲染成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"}'

 

Deserializing objects

反序列化也很相似。首先咱們解析流到Python源生的數據類型。

import io
from rest_framework.parsers import JSONParser

stream = io.BytesIO(json)
data = JSONParser().parse(stream)

而後存儲這些數據到一個驗證過數據的字典中

serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}

若是想返回完整的基於驗證過的數據的對象實例,須要執行.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)

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

若是你的對象實例對應Django模型,你也想確保這些方法保存對象到數據庫。若是Comment是一個Django的model,這些方法可能看起來像

def create(self, validated_data):
        return Comment.objects.create(**validated_data)

    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()
        return instance

這裏就把最前面Comment類的創建拋棄了。徹底把它看成一個model。如今當咱們要反序列化數據,調用.save()方法返回一個對象實例,基於驗證的數據

comment = serializer.save()

調用.save()方法可能會建立一個新的實例,或者更新一個現有的實例,取決於當初始化serializer類時是否已經存在一個實例。

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

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

.create()和.update()方法都是可選擇的,不管你怎麼選擇,取決於serializer類的使用狀況。

 

Passing additional attributes to .save()

有時你想要你的視圖代碼能夠在保存實例的時候注入新的額外數據。這些額外的數據可能包含當前用戶,當前時間或者其餘不是request.data一部分的東西。

能夠像這樣操做

serializer.save(owner=request.user)

當調用.create()或.update()時,任何額外的關鍵字參數都將包含在validated_data參數中。

 

Overriding .save() directly

有時候現有的方法不可以知足需求,好比在保存數據的時候,須要發送一封郵件,提醒別人更新的內容

class ContactForm(serializers.Serializer):
    email = serializers.EmailField()
    message = serializers.CharField()

    def save(self):
        email = self.validated_data['email']
        message = self.validated_data['message']
        send_email(from=email, message=message)

注意,在這個示例裏面,咱們已經能夠直接訪問serializer的.validated_data了。在serializer執行過程當中,是先驗證後操做(更新,保存),從常理上想其實也是這樣,跟Form同樣。

 

Validation

當反序列化數據你老是須要在視圖訪問驗證過的數據或保存一個對象實例以前先調用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.']}

字典裏全部的key都是serializer的字段名,值是與之對應的任何報錯信息的字符串列表。有可能會有一個non_field_errors的關鍵字,它會列出來總體上的驗證錯誤。non_field_errors關鍵字的名稱能夠經過使用NON_FIELD_ERRORS_KEY在REST framework的設置裏面自定義。

當反序列化一組列表項目時,錯誤信息將會返回一個包含字典的列表,與反序列化的項目一一對應,即queryset的裏面每一個實例

Raising an exception on invalid data

.is_valid()方法有一個raise_exception的標識,若是有驗證錯誤信息,將會引起一個serializers.ValidationError報錯。這些錯誤將會自動被REST framework提供的默認錯誤處理器處理,並默認返回HTTP 400 Bad Request的響應

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

在我看來,上面的那種報錯,意義不大,對於熟悉form的來講,跟form類似,咱們應該更關注的是字段級別的錯誤信息總體組合錯誤這兩類,如今就來剖析這兩點Field-level validation & object-level validation

Field-level validation

你能夠本身添加字段級別的驗證經過.validate_<field_name>方法在你的Serializer子類裏面,這跟form裏面的字段驗證.clean_<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

若是在serializer申明的<field_name>帶有參數required=False,若是不包括此字段,則不會執行此驗證步驟。

Object-level validation

關於須要訪問多字段的驗證,須要添加一個.validate()方法在你的serializer的子類裏面,這個方法裏面有一個單獨的參數,是字段的字典。若是必要的話須要引起serializers.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

值得一提的是form驗證的幾個階段在serializer裏面也都有,這裏再提一個字段基礎驗證。

Validators

單個字段在serializer能夠包含驗證器經過在字典實例上申明

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

Serializer類也能夠包含重用的驗證器來驗證全局的字段數據。這些驗證器被包含在內部的Meta類裏面

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

    class Meta:
        #驗證一個房間天天只能有一個事件
        validators = UniqueTogetherValidator(
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

 

 

Partial Updates

默認,serializer必須經過全部須要的字段值,不然講引起驗證錯誤。可是你可使用partial參數來容許部分更新。

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

 

Dealing with nested objects

以前的示例都是利用serializer處理簡單的數據類型,但有時咱們也須要表現更加複雜的對象,而不只僅是字符串,數字,日期等等。好比如今要舉例的就是對於嵌套的對象的處理。

下面的serializer類自己有一個字段用來標識跟另外一個對象嵌套的關係

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

若是嵌套的對象是可選擇接受None值,你能夠傳入required=False

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # May be an anonymous user.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

若是嵌套的展示是一個列表組的時候,你應該傳入many=True

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)
    edits = EditItemSerializer(many=True)  # A nested list of 'edit' items.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

 

Writable nested representations

當處理支持反序列化數據的嵌套表示,嵌套對象的任何錯誤講嵌套在該嵌套對象的字段名下

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

一樣的驗證過的數據.validated_data屬性也是這樣的嵌套結構

writing .create() methods for nested representations

若是支持可寫的嵌套表示,須要寫.create()或者.update()方法來處理保存多個對象。下面的示例,展現瞭如何處理建立user的嵌套對象

class UserSerializer(serializers.ModelSerializer):
    profile = ProfileSerializer()

    class Meta:
        model = User
        fields = ('username', 'email', 'profile')

    def create(self, validated_data):
        profile_data = validated_data.pop('profile')
        user = User.objects.create(**validated_data)
        Profile.objects.create(user=user, **profile_data)
        return user

Writing .update() methods for nested representations

處理更新須要當心點,好比數據的關係是None,或者沒有提供,該怎麼作?

  def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # Unless the application properly enforces that this field is
        # always set, the follow could raise a `DoesNotExist`, which
        # would need to be handled.
        profile = instance.profile

        instance.username = validated_data.get('username', instance.username)
        instance.email = validated_data.get('email', instance.email)
        instance.save()

        profile.is_premium_member = profile_data.get(
            'is_premium_member',
            profile.is_premium_member
        )
        profile.has_support_contract = profile_data.get(
            'has_support_contract',
            profile.has_support_contract
         )
        profile.save()

        return instance

由於嵌套行爲建立和更新有點模糊不清,可能須要在相關模型間複雜的依賴。REST framework3須要你老是清楚的寫下這些方法。默認ModelSerializer .create()和.update()方法不包含對可嵌套表現的支持。

可是,第三方包是能夠作到的,好比DRF Writable Nested支持自動可寫的嵌套表示

 

Handling saving related instances in model manager classes

serialize中保存多個相關實例的替代方法是編寫處理建立正確實例的自定義模型管理器類。

假設咱們要確保User實例跟Profile實例老是一塊兒成對建立,能夠建立一個自定義manager類

class UserManager(models.Manager):
    ...

    def create(self, username, email, is_premium_member=False, has_support_contract=False):
        user = User(username=username, email=email)
        user.save()
        profile = Profile(
            user=user,
            is_premium_member=is_premium_member,
            has_support_contract=has_support_contract
        )
        profile.save()
        return user

這個管理器類如今更精細地封裝了用戶實例和配置文件實例老是同時建立的。如今,咱們在serializer類裏的.create()方法能夠被重寫使用新的manager方法

class User(models.Model):
    objects=UserManager()
    .......
def create(self, validated_data):
    return User.objects.create(
        username=validated_data['username'],
        email=validated_data['email']
        is_premium_member=validated_data['profile']['is_premium_member']
        has_support_contract=validated_data['profile']['has_support_contract']
    )

關於model managers詳細。。

Dealing with multiple objects

序列化對象列表

queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
#     {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
#     {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
#     {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]

反序列化對象列表

反序列化多個對象的默認行爲是支持多個對象建立,但不支持多個對象更新。更多信息查看下文的ListSerializer文檔

Including extra context

額外的內容添加到序列化數據內,在初始化serializer類的時候傳入context參數

serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}

內容字典context能夠被使用在任何serializer字典邏輯內,好比.to_representation()方法,經過self.contenxt屬性

 

二、ModelSerializer

讓serializer跟Django內定義的model的映射關係更緊密。

ModelSerializer類提供一個捷徑在你建立serializer類並讓字段跟Model的字段自動一一對應。ModelSerializer跟正常的Serializer類幾乎同樣,除了:

1、自動生成了一系列的字典,基於model模型
2、自動爲serializer生成驗證器,好比unique_together驗證器
三、默認包含簡單的.create().update()執行方法
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')

導入ModelSerializer

>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
    id = IntegerField(label='ID', read_only=True)
    name = CharField(allow_blank=True, max_length=100, required=False)
    owner = PrimaryKeyRelatedField(queryset=User.objects.all())

Specifying which fields to include

方便快捷的自定義哪些字段是須要的,fields爲'__all__'表明全部, exclude表示須要排除的,即model內其餘的都須要

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = '__all__'
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        exclude = ('users',)

 

Specifying nested serialization

默認ModelSerializer在關係中使用主鍵,可是你能夠很輕易的生成嵌套表示,使用depth

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        depth = 1

depth選項應該設置爲整數值,該整數值指示在恢復到平面表示以前應該遍歷的關係的深度。若是你須要客製化的完成序列化,你講須要本身去定義這個字段

 

Specify fields explicitly

你能夠添加額外的字段到ModelSerializer,或者經過在類裏面申明字段來重寫默認字段,跟Serializer同樣

class AccountSerializer(serializers.ModelSerializer):
    url = serializers.CharField(source='get_absolute_url', read_only=True)
    groups = serializers.PrimaryKeyRelatedField(many=True)

    class Meta:
        model = Account

額外的字段能夠對應任何屬性或在模型上是可調用的。

Specifying read only fields

也許你想要讓多個字段變成read-only,你能夠添加read_only=True屬性到每一個須要的字段內,或者使用Meta的選項字段,read_only_fields. 這個選項必須是列表或者元祖

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        read_only_fields = ('account_name',)

若是Model字段內有editable=False的設定,自動拉去的字段就會設置read-only屬性,而再也不須要添加read_only_fields選項

注意,有個特殊的案例,當只讀字段是model級別聯合惟一unique_together約束中的一部分。在這個案例裏面這個字段是serializer類所須要的爲了驗證約束,可是它同時也是用戶不可編輯的

正確的處理方式是清楚的在serializer裏面標註出來這個字段,提供read_only=Truedefault=..兩個關鍵字參數。一個示例對於這種只讀關係,當前被驗證的User跟另外一個標識符是聯合惟一unique_together

user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())

Additional keyword arguments

還有一個捷徑容許制定任意額外的關鍵字在字段上,使用extra_kwargs選項。好比這個案例裏面的read_only_fields,它標識你不須要清楚的定義這個字段在serializer上。這個選項是一個字段映射字段名稱到一個關鍵字參數字典

class CreateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('email', 'username', 'password')
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User(
            email=validated_data['email'],
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()
        return user

 

三、HyperlinkedModelSerializer

HyperlinkedModelSerializer類和ModelSerializer類很像,除了相比較主鍵它使用超連接來標識關係,serializer默認會包括一個url字段代替主鍵字段。url字段使用HyperlinkedModelSerializer serializer字段來表示。

url字段會被用HyperlinkedIdentityField serializer字段表示,模型上的任何關係將被用HyperlinkedRelatedField serializer字段表示。你能夠直接把主鍵添加到fields選項

class UserSerializer(HyperlinkedModelSerializer):

    class Meta:
        model = PersonResource
        fields = ('id', 'name', 'age', 'job', 'url', 'classes')

說到這裏你們估計已經對示例亂了,這邊從新給一份

 1 # serializers.py
 2 class PersonModelSerializer(ModelSerializer):
 3 
 4     class Meta:
 5         model = PersonResource
 6         fields = ('id', 'name', 'age', 'get_gender', 'get_job', 'modify_time')
 7 
 8 class UserSerializer(HyperlinkedModelSerializer):
 9 
10     class Meta:
11         model = PersonResource
12         fields = ('id', 'name', 'age', 'job', 'url', 'classes')
13 
14 class ClassesSerializer(ModelSerializer):
15 
16     class Meta:
17         model = Classes
18         fields = ('id', 'name', 'get_lesson', 'days')
19 
20 
21 # views.py
22 class UserHyperlinkView(ListAPIView):
23     queryset = PersonResource.objects.filter(job=1)
24     serializer_class = UserSerializer
25 
26 class UserDetailView(RetrieveAPIView):
27     queryset = PersonResource.objects.filter(job=1)
28     serializer_class = PersonModelSerializer
29 
30 class ClassDetailView(RetrieveAPIView):
31     queryset = Classes.objects.all()
32     serializer_class = ClassesSerializer
33 
34 
35 # models.py
36 class PersonResource(models.Model):
37     name = models.CharField(max_length=128)
38     age = models.PositiveSmallIntegerField()
39     gender_type = ((1, ''), (2, ''))
40     gender = models.PositiveSmallIntegerField(choices=gender_type, default=1)
41     job_type = ((1, '學生'), (2, '教師'))
42     job = models.PositiveSmallIntegerField(choices=job_type, default=1)
43     classes = models.ForeignKey('Classes', on_delete=models.CASCADE)
44     modify_time = models.DateTimeField(auto_now=True)
45 
46     def __str__(self):
47         return '%s - %s' % (self.name, self.get_job_display())
48 
49     def get_gender(self):
50         return self.get_gender_display()
51 
52     def get_job(self):
53         return self.get_job_display()
54 
55 class Classes(models.Model):
56     name = models.CharField(max_length=128)
57     lesson_type = ((0, 'Python'), (1, 'linux'), (2, 'GO'), (3, 'R'))
58     lesson = models.PositiveSmallIntegerField(choices=lesson_type)
59     days = models.PositiveSmallIntegerField()
60 
61     def __str__(self):
62         return '%s - %s' % (self.name, self.get_lesson_display())
63 
64     def get_lesson(self):
65         return self.get_lesson_display()
View Code

這個HyperlinkedModelSerializer裏面有兩個字段選項參數:

url     ==> 指向的是 自己,做用是抓取自己的具體數據,因此這裏須要指向一個RetrieveAPIView
classes  ==>    外鍵,指向的另外的一個關聯的model,因此這裏也須要一個獨立的顯示另一個model詳細信息的RetrieveAPIView。

因此文件內的內容就很好理解了,咱們須要三個V視圖,一個服務於HyperlinkedModelSerializer,另外兩個就是上面的,用來服務於,RetrieveAPIView。

如今還有一個問題,既然知道須要的url的行爲是什麼了,在一整個項目的urls.py文件中,如何斷定用哪一個url呢?

這裏HyperlinkedModelSerializer裏面的默認是使用{model_name}-detail去匹配抓取的,上面的文件內一直沒有把最後的路由關係呈現給你們。

urlpatterns = [
    path('student/', UserHyperlinkView.as_view()),
    path('student/<int:pk>/', UserDetailView.as_view(), name='personresource-detail'),
    path('classes/<int:pk>/', ClassDetailView.as_view(), name='classes-detail'),
]

如今對於簡單的HyperLinkedModelSerializer應該一目瞭然。

 

Absolute and relative URLS

當初始化一個HyperlinkedModelSerializer,你必須包含整個請求在serializer內容中

serializer = AccountSerializer(queryset, context={'request': request})

這樣作會讓你的超連接有一個合適的域名,一遍結果的顯示是使用整個合格的URL,好比

http://api.example.com/accounts/1/

而不是相對的url

/accounts/1/

若是你想要用相對的rl,你應該明確的傳入{'request': None}參數在serializer context裏面。可是上面的實例裏面,明顯咱們並無傳入這些參數,依樓主猜想,在使用View層視圖的時候,這一層已經被包裝在內部了,當上面一段是廢話就行了,反正我以爲用處不大,應該出來的都是絕對路徑。

 

How hyperlinked views are determined

默認超連接指望和知足'{model_name}-detail'風格的視圖名稱相對應,並經過pk關鍵字參數超着實例。你能夠重寫URL字段的視圖名和查找字段經過extra_kwarg設置裏的使用view_namelookup_field選項:

class UserSerializer(HyperlinkedModelSerializer):

    class Meta:
        model = PersonResource
        fields = ('id', 'name', 'age', 'job')
        extra_kwargs = {
            'url': {'view_name': 'aaa', 'lookup_field': 'name'},
            'classes': {'lookup_field': 'name'}
        }

因此熟悉一點rest ramework,再看完上面示例,應該很靈性的知道這裏的設置是怎麼一回事了。首先url層,須要修改一下user detail做用的name改爲aaa,而後url參數名都改爲name並在 view層添加lookup_field

# urls.py
urlpatterns = [
    path('student/', UserHyperlinkView.as_view()),
    path('student/<str:name>/', UserDetailView.as_view(), name='aaa'),
    path('classes/<str:name>/', ClassDetailView.as_view(), name='classes-detail'),
]

# view.py
class UserDetailView(RetrieveAPIView):
    queryset = PersonResource.objects.filter(job=1)
    serializer_class = PersonModelSerializer
    lookup_field = ('name')


class ClassDetailView(RetrieveAPIView):
    queryset = Classes.objects.all()
    serializer_class = ClassesSerializer
    lookup_field = ('name')

你也能夠設置此字段在serializer上。

class UserSerializer(HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='aaa',
        lookup_field='name'
    )
    classes = serializers.HyperlinkedRelatedField(
        lookup_field='name',
        view_name='classes-detail',
        read_only=True, 
        many=True,  # 若是對應對個對象須要加many=True
    )

    class Meta:
        model = PersonResource
        fields = ('id', 'name', 'age', 'job', 'url', 'classes')

注意

  一、url對應的是HyperlinkedIdentityField,而classes對應的是HyperlinkedRelatedField

  二、若是按照上面這種寫法的話view_name, lookup_field就是必填的了,並且外鍵必須添加read_only=True, 至於many=True就更好理解了。

  三、還有一點細節就是這兩種方法裏面,注意一下Meta fields選項裏面這兩個參數,若是是在serializer上面申明的必須fields裏面也填寫這個參數,若是是extra_kwargs,直接在裏面設定好就行。理解一下也很好記憶   

若是想要修改URL字段名,即修改'url',你能夠經過使用全局設置URL_FIELD_NAME,看起來意義不大

 

 四、ListSerializer

ListSerializer類同時提供的序列化和驗證多對象的行爲。一般你不須要直接使用ListSerializer,而僅僅是在初始化serializer的時候傳入many=True參數。當serializer被實例化並傳入many=True參數是,ListSerializer實例就會被建立。序列化的類就會變成ListSerializer的子類。

allow_empty參數也能夠被傳入ListSerializer或者帶有many=True參數的serializer。默認是True,可是也能夠設置爲False,若是你不容許空的list做爲合法的輸入。

 

Customizing ListSerializer behavior

當你想客製化ListSerializer的行爲時有一些使用案例

一、你想對列表提供特殊的驗證,好比檢查某個元素和列表中的另外一個元素不矛盾。

二、你想要客製化的對多個對象進行建立或者更新行爲

對於這些案例你能夠修改這些傳入many=True參數的類,經過使用serializer的Meta類上的list_serializer_class選項。

class CustomListSerializer(serializers.ListSerializer):
    ...

class CustomSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = CustomListSerializer

Customizing multiple create

默認對於多對象建立的執行是簡單的爲每個列表中的對象調用.create()方法. 若是你想自定製這個行爲,你能夠客製化ListSerializer類上的.create()方法

class BookListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        books = [Book(**item) for item in validated_data]
        return Book.objects.bulk_create(books)

class BookSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = BookListSerializer

Customizing multiple update

ListSerializer默認不支持多對象更新,由於中間須要考慮到以什麼爲依據去斷定是更新,建立仍是刪除一個對象。

你須要清楚地添加一個id字段。默認保留生成id字段被標記成只讀。因此在更新的時候應該被移除。當你把這些定義清楚,在你的list serializer裏的update方法就可用了。

下面有一個你能夠選擇用來對多對象進行更新的方法。

class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        # Maps for id->instance and id->data item.
        book_mapping = {book.id: book for book in instance}
        data_mapping = {item['id']: item for item in validated_data}

        # Perform creations and updates.
        ret = []
        for book_id, data in data_mapping.items():
            book = book_mapping.get(book_id, None)
            if book is None:
                ret.append(self.child.create(data))
            else:
                ret.append(self.child.update(book, data))

        # Perform deletions.
        for book_id, book in book_mapping.items():
            if book_id not in data_mapping:
                book.delete()

        return ret

class BookSerializer(serializers.Serializer):
    # We need to identify elements in the list using their primary key,
    # so use a writable field here, rather than the default which would be read-only.
    id = serializers.IntegerField()
    ...

    class Meta:
        list_serializer_class = BookListSerializer

第三方包,3.1版本可能提供了一些自動支持對對象更新的操做,跟REST framework2裏面的allow_add_remove行爲很像。

 

Customizing ListSerializer initialization

當一個many=True的serializer被初始化的時候,咱們須要決定那些字段和關鍵字參數應該傳入子類(serializer)和父類(ListSerializer)的__init__()方法,默認是所有字段傳給兩個類,除了驗證器和其餘自定義關鍵字參數。

你也許要清楚的代表當傳入many=True時子類和父類如何被初始化,你可使用many_init類方法

    @classmethod
    def many_init(cls, *args, **kwargs):
        # Instantiate the child serializer.
        kwargs['child'] = cls()
        # Instantiate the parent list serializer.
        return CustomListSerializer(*args, **kwargs)

 

BaseSerializer

這個類實現了基本相同的API類的序列化程序:

.data         - Returns the outgoing primitive representation.
.is_valid()      - Deserializes and validates incoming data.
.validated_data   - Returns the validated incoming data.
.errors        - Returns any errors during validation.
.save()        - Persists the validated data into an object instance

有四個能夠被重寫的方法,根據你想要實現的serializer類的支持的功能決定

.to_representation() - 重寫爲了支持序列化,爲讀操做
.to_internal_value() - 重寫爲了支持反序列化,爲寫操做
.create() and .update() - 重寫它們中的一個或多個來支持保存實例

由於這個類跟Serializer類提供了同樣的接口,你可使用在基於類的通用視圖上,當想要使用經常使用的Serializer或ModelSerializer類。惟一的不一樣就是BaseSerializer類不會生產可瀏覽的HTML表單API。

只讀BaseSerializer類

使用BaseSerializer類執行只讀serializer,只須要重寫.to_representation()方法。經過Django model簡單看個例子

class UserBaseSerializer(BaseSerializer):
    def to_representation(self, instance):
        return {
            'name': instance.name,
            'age': instance.age
        }
@api_view(['GET'])
def base_user(request, pk):
    instance = PersonResource.objects.get(pk=pk)
    serializer = UserBaseSerializer(instance)
    return Response(serializer.data)

或者若是多實例:

@api_view(['GET'])
def base_user1(request):
    queryset = PersonResource.objects.order_by('id')
    serializer = UserBaseSerializer(queryset, many=True)
    return Response(serializer.data)

可讀寫的BaseSerializer類

建立讀寫的serializer首先不要執行.to_internal_value(), 基礎的驗證API將會在這個serializer上可用,你可使用.is_valid(), .validated_data.errors

若是你想要支持.save()方法,你將須要執行.create().update()方法中的一個或者多個

class UserBaseSerializer(BaseSerializer):
    def to_internal_value(self, data):
        name = data.get('name')
        age = int(data.get('age'))

        if not name:
            raise  ValidationError(
                {'name': 'this field is required.'}
            )
        if not age:
            raise ValidationError({
                'age': 'this field is required.'
            })
        if age > 25:
            raise ValidationError({
                'age': 'age may not bigger than 25.'
            })
        return {
            'name': name,
            'age': age
        }

    def to_representation(self, instance):
        return {
            'name': instance.name,
            'age': instance.age,
        }

    def create(self, validated_data):
        return PersonResource.objects.create(**validated_data)

Creating new base classes

在你想要創建新的通用serializer類來處理特殊的序列化格式或者和可選擇的存儲後臺集成時BaseSerializer類也頗有用

class ObjectSerializer(serializers.BaseSerializer):
    """
    A read-only serializer that coerces arbitrary complex objects
    into primitive representations.
    """
    def to_representation(self, obj):
        for attribute_name in dir(obj):
            attribute = getattr(obj, attribute_name)
            if attribute_name('_'):
                # Ignore private attributes.
                pass
            elif hasattr(attribute, '__call__'):
                # Ignore methods and other callables.
                pass
            elif isinstance(attribute, (str, int, bool, float, type(None))):
                # Primitive types can be passed through unmodified.
                output[attribute_name] = attribute
            elif isinstance(attribute, list):
                # Recursively deal with items in lists.
                output[attribute_name] = [
                    self.to_representation(item) for item in attribute
                ]
            elif isinstance(attribute, dict):
                # Recursively deal with items in dictionaries.
                output[attribute_name] = {
                    str(key): self.to_representation(value)
                    for key, value in attribute.items()
                }
            else:
                # Force anything else to its string representation.
                output[attribute_name] = str(attribute)

 

Advanced serializer usage

重寫序列化和反序列化行爲。若是有須要,你能夠重寫.to_representation()或者.to_internal_value()方法。

好比,輕輕地繼承一下這個方法,並把name轉換成小寫字符串

.to_representation()

def to_representation(self, instance):
    """Convert `username` to lowercase."""
    ret = super().to_representation(instance)
    ret['name'] = ret['name'].lower()
    return ret

.to_internal_value()

帶着沒有驗證的數據做爲輸入,應該返回驗證過的數據並讓serializer.validated_data可用.這個返回值也將被傳入.create()或者.update()方法,若是.save()方法被在這個serializer類上調用。

若是想作對象級別的驗證,推薦你重寫.validate()方法

數據經過這個方法正常會變成request.data的值

 

Serializer Inheritance

跟form同樣,你能夠經過繼承對serializers進行擴展或者重用。它容許你申明一些字段或者方法在父類上能夠被其餘serializers使用。

class MyBaseSerializer(Serializer):
    my_field = serializers.CharField()

    def validate_my_field(self, value):
        ...

class MySerializer(MyBaseSerializer):
    ...

跟Django的Model和ModelForm類同樣,serializer內部的Meta類不是隱式的繼承自它父類的Meta類。你必須明確的繼承它的父類。

class AccountSerializer(MyBaseSerializer):
    class Meta(MyBaseSerializer.Meta):
        model = Account

一般,咱們不推薦使用繼承在內部的Meta類,而是顯示地申明全部選項代替。

另外,關於serializer繼承的一些警告

一、標準的Python命名解決規則應用。若是你有多個基類定義了內部的Meta類,只有第一個的會被使用。這表示,子類若是存在Meta則是自己的,不然是第一個父類的Meta

二、能夠申明性地移除字段從繼承的父類,經過設定名稱爲None在子類上。

class MyBaseSerializer(ModelSerializer):
    my_field = serializers.CharField()

class MySerializer(MyBaseSerializer):
    my_field = None

 

Dynamically modify fields

經過.fields屬性對字段隨心所欲。

class DynamicFieldsModelSerializer(serializers.ModelSerializer):

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

可能上面的看着有點迷糊

>>> class UserSerializer(DynamicFieldsModelSerializer):
>>>     class Meta:
>>>         model = User
>>>         fields = ('id', 'username', 'email')
>>>
>>> print UserSerializer(user)
{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
>>>
>>> print UserSerializer(user, fields=('id', 'email'))
{'id': 2, 'email': 'jon@example.com'}

上面的實例就只能顯示fields裏面定義的字段了。

相關文章
相關標籤/搜索