官方原文連接
本系列文章 github 地址
轉載請註明出處html
關係字段用於表示模型關係。 它們能夠應用於 ForeignKey
,ManyToManyField
和 OneToOneField
關係,反向關係以及 GenericForeignKey
等自定義關係。python
注意: 關係字段在
relations.py
中聲明,但按照慣例,你應該從serializers
模塊導入它們,使用from rest_framework import serializers
引入,並像serializers.<FieldName>
這樣引用字段。git
在使用 ModelSerializer
類時,將自動爲你生成序列化字段和關係。檢查這些自動生成的字段能夠學習如何定製關係風格。github
爲此,使用 python manage.py shell
打開 Django shell,而後導入序列化類,實例化它並打印對象表示形式...shell
>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print repr(serializer) # Or `print(repr(serializer))` in Python 3.x.
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
複製代碼
爲了解釋各類類型的關係字段,咱們將爲咱們的示例使用幾個簡單的模型。咱們的模型將使用音樂專輯,以及每張專輯中列出的曲目。django
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
class Meta:
unique_together = ('album', 'order')
ordering = ['order']
def __unicode__(self):
return '%d: %s' % (self.order, self.title)
複製代碼
StringRelatedField
用於使用 __unicode__
方法表示關係。json
例如,下面的序列化類。api
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
複製代碼
將序列化爲如下形式。bash
{
'album_name': 'Things We Lost In The Fire',
'artist': 'Low',
'tracks': [
'1: Sunflower',
'2: Whitetail',
'3: Dinosaur Act',
...
]
}
複製代碼
該字段是隻讀的。app
參數:
many
- 若是是一對多的關係,就將此參數設置爲 True
.PrimaryKeyRelatedField
用於使用其主鍵表示關係。
例如,如下序列化類:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
複製代碼
將序列化爲這樣的表示:
{
'album_name': 'Undun',
'artist': 'The Roots',
'tracks': [
89,
90,
91,
...
]
}
複製代碼
默認狀況下,該字段是可讀寫的,但您可使用 read_only
標誌更改此行爲。
參數:
queryset
- 驗證字段輸入時用於模型實例查詢的查詢集。必須顯式地設置查詢集,或設置 read_only=True
。many
- 若是應用於一對多關係,則應將此參數設置爲 True
。allow_null
- 若是設置爲 True
,那麼該字段將接受 None
值或可爲空的關係的空字符串。默認爲 False
。pk_field
- 設置爲一個字段來控制主鍵值的序列化/反序列化。例如, pk_field=UUIDField(format='hex')
會將 UUID 主鍵序列化爲其緊湊的十六進制表示形式。HyperlinkedRelatedField
用於使用超連接來表示關係。
例如,如下序列化類:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='track-detail'
)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
複製代碼
將序列化爲這樣的表示:
{
'album_name': 'Graceland',
'artist': 'Paul Simon',
'tracks': [
'http://www.example.com/api/tracks/45/',
'http://www.example.com/api/tracks/46/',
'http://www.example.com/api/tracks/47/',
...
]
}
複製代碼
默認狀況下,該字段是可讀寫的,但您可使用 read_only
標誌更改此行爲。
注意:該字段是爲映射到接受單個 URL 關鍵字參數的 URL 的對象而設計的,如使用 lookup_field
和 lookup_url_kwarg
參數設置的對象。
這適用於包含單個主鍵或 slug 參數做爲 URL 一部分的 URL。
若是須要更復雜的超連接表示,你須要自定義該字段,稍後會詳解。
參數:
view_name
- 用做關係目標的視圖名稱。若是你使用的是標準路由器類,則這將是一個格式爲 <modelname>-detail
的字符串。必填.queryset
- 驗證字段輸入時用於模型實例查詢的查詢集。必須顯式地設置查詢集,或設置 read_only=True
。many
- 若是應用於一對多關係,則應將此參數設置爲 True
。allow_null
- 若是設置爲 True
,那麼該字段將接受 None
值或可爲空的關係的空字符串。默認爲 False
。lookup_field
- 用於查找的目標字段。對應於引用視圖上的 URL 關鍵字參數。默認是 'pk'
.lookup_url_kwarg
- 與查找字段對應的 URL conf 中定義的關鍵字參數的名稱。默認使用與 lookup_field
相同的值。format
- 若是使用格式後綴,則超連接字段將使用與目標相同的格式後綴,除非使用 format
參數進行覆蓋。SlugRelatedField
用於使用目標上的字段來表示關係。
例如,如下序列化類:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='title'
)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
複製代碼
將序列化爲這樣的表示:
{
'album_name': 'Dear John',
'artist': 'Loney Dear',
'tracks': [
'Airport Surroundings',
'Everything Turns to You',
'I Was Only Going Out',
...
]
}
複製代碼
默認狀況下,該字段是可讀寫的,但您可使用 read_only
標誌更改此行爲。
將 SlugRelatedField
用做讀寫字段時,一般須要確保 slug 字段與 unique=True
的模型字段相對應。
參數:
slug_field
- 用來表示目標的字段。這應該是惟一標識給定實例的字段。例如, username
。必填queryset
- 驗證字段輸入時用於模型實例查詢的查詢集。必須顯式地設置查詢集,或設置 read_only=True
。many
- 若是應用於一對多關係,則應將此參數設置爲 True
。allow_null
- 若是設置爲 True
,那麼該字段將接受 None
值或可爲空的關係的空字符串。默認爲 False
。此字段能夠做爲身份關係應用,例如 HyperlinkedModelSerializer
上的 'url'
字段。它也能夠用於對象的屬性。例如,如下序列化類:
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')
class Meta:
model = Album
fields = ('album_name', 'artist', 'track_listing')
複製代碼
將序列化爲這樣的表示:
{
'album_name': 'The Eraser',
'artist': 'Thom Yorke',
'track_listing': 'http://www.example.com/api/track_list/12/',
}
複製代碼
該字段始終爲只讀。
參數:
view_name
- 用做關係目標的視圖名稱。若是你使用的是標準路由器類,則這將是一個格式爲 <modelname>-detail
的字符串。必填。lookup_field
- 用於查找的目標字段。對應於引用視圖上的 URL 關鍵字參數。默認是 'pk'
。lookup_url_kwarg
- 與查找字段對應的 URL conf 中定義的關鍵字參數的名稱。默認使用與 lookup_field
相同的值。format
- 若是使用格式後綴,則超連接字段將使用與目標相同的格式後綴,除非使用 format
參數進行覆蓋。嵌套關係能夠經過使用序列化類做爲字段來表達。
若是該字段用於表示一對多關係,則應將 many=True
標誌添加到序列化字段。
例如,如下序列化類:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ('order', 'title', 'duration')
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
複製代碼
將序列化爲這樣的嵌套表示:
>>> album = Album.objects.create(album_name="The Grey Album", artist='Danger Mouse')
>>> Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)
<Track: Track object>
>>> Track.objects.create(album=album, order=2, title='What More Can I Say', duration=264)
<Track: Track object>
>>> Track.objects.create(album=album, order=3, title='Encore', duration=159)
<Track: Track object>
>>> serializer = AlbumSerializer(instance=album)
>>> serializer.data
{
'album_name': 'The Grey Album',
'artist': 'Danger Mouse',
'tracks': [
{'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
{'order': 2, 'title': 'What More Can I Say', 'duration': 264},
{'order': 3, 'title': 'Encore', 'duration': 159},
...
],
}
複製代碼
默認狀況下,嵌套序列化類是隻讀的。若是要支持對嵌套序列化字段的寫操做,則須要建立 create()
和/或 update()
方法,以明確指定應如何保存子關係。
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ('order', 'title', 'duration')
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
def create(self, validated_data):
tracks_data = validated_data.pop('tracks')
album = Album.objects.create(**validated_data)
for track_data in tracks_data:
Track.objects.create(album=album, **track_data)
return album
>>> data = {
'album_name': 'The Grey Album',
'artist': 'Danger Mouse',
'tracks': [
{'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
{'order': 2, 'title': 'What More Can I Say', 'duration': 264},
{'order': 3, 'title': 'Encore', 'duration': 159},
],
}
>>> serializer = AlbumSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.save()
<Album: Album object>
複製代碼
在極少數狀況下,現有的關係類型都不符合您須要的表示形式,你能夠實現一個徹底自定義的關係字段,該字段準確描述應該如何從模型實例生成輸出表示。
要實現自定義關係字段,您應該重寫 RelatedField
,並實現 .to_representation(self, value)
方法。此方法將字段的目標做爲 value
參數,並返回應用於序列化目標的表示。value
參數一般是一個模型實例。
若是要實現讀寫關係字段,則還必須實現 .to_internal_value(self, data)
方法。
要提供基於 context
的動態查詢集,還能夠覆蓋 .get_queryset(self)
,而不是在類上指定 .queryset
或初始化該字段。
例如,咱們能夠定義一個關係字段,使用它的順序,標題和持續時間將音軌序列化爲自定義字符串表示。
import time
class TrackListingField(serializers.RelatedField):
def to_representation(self, value):
duration = time.strftime('%M:%S', time.gmtime(value.duration))
return 'Track %d: %s (%s)' % (value.order, value.name, duration)
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackListingField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
複製代碼
將序列化爲這樣的表示:
{
'album_name': 'Sometimes I Wish We Were an Eagle',
'artist': 'Bill Callahan',
'tracks': [
'Track 1: Jim Cain (04:39)',
'Track 2: Eid Ma Clack Shaw (04:19)',
'Track 3: The Wind and the Dove (04:34)',
...
]
}
複製代碼
在某些狀況下,您可能須要自定義超連接字段的行爲,以表示須要多個查詢字段的 URL。
您能夠經過繼承 HyperlinkedRelatedField
來實現此目的。有兩個能夠被覆蓋的方法:
get_url(self, obj, view_name, request, format)
get_url
方法用於將對象實例映射到其 URL 表示。
若是 view_name
和 lookup_field
屬性未配置爲正確匹配 URL conf,可能會引起 NoReverseMatch
。
get_object(self, queryset, view_name, view_args, view_kwargs)
若是您想支持可寫的超連接字段,那麼您還須要重寫 get_object
,以便將傳入的 URL 映射回它們表示的對象。對於只讀超連接字段,不須要重寫此方法。
此方法的返回值應該是與匹配的 URL conf 參數對應的對象。
可能會引起 ObjectDoesNotExist
異常。
假設咱們有一個帶有兩個關鍵字參數的 customer 對象的 URL,以下所示:
/api/<organization_slug>/customers/<customer_pk>/
複製代碼
這沒辦法用僅接受單個查找字段的默認實現來表示。
在這種狀況下,咱們須要繼承 HyperlinkedRelatedField
並重寫其中的方法來得到咱們想要的行爲:
from rest_framework import serializers
from rest_framework.reverse import reverse
class CustomerHyperlink(serializers.HyperlinkedRelatedField):
# We define these as class attributes, so we don't need to pass them as arguments.
view_name = 'customer-detail'
queryset = Customer.objects.all()
def get_url(self, obj, view_name, request, format):
url_kwargs = {
'organization_slug': obj.organization.slug,
'customer_pk': obj.pk
}
return reverse(view_name, kwargs=url_kwargs, request=request, format=format)
def get_object(self, view_name, view_args, view_kwargs):
lookup_kwargs = {
'organization__slug': view_kwargs['organization_slug'],
'pk': view_kwargs['customer_pk']
}
return self.get_queryset().get(**lookup_kwargs)
複製代碼
請注意,若是您想將此風格與通用視圖一塊兒使用,那麼您還須要覆蓋視圖上的 .get_object
以得到正確的查找行爲。
通常來講,咱們建議儘量在 API 表示方式下使用平面風格,但嵌套 URL 風格在適度使用時也是合理的。
queryset
參數queryset
參數只對可寫關係字段是必需的,在這種狀況下,它用於執行模型實例查找,該查找從基本用戶輸入映射到模型實例。
在 2.x 版本中,若是正在使用 ModelSerializer
類,則序列化類有時會自動肯定 queryset
參數。
此行爲現在替換爲始終爲可寫關係字段使用顯式 queryset
參數。
這樣作能夠減小 ModelSerializer
提供的隱藏 「魔術」 數量(指 ModelSerializer
在內部幫咱們完成的工做),使字段的行爲更加清晰,並確保在使用 ModelSerializer
快捷方式(高度封裝過,使用簡單)或使用徹底顯式的 Serializer
類之間轉換是微不足道的。
模型內置的 __str__
方法用來生成用於填充 choices
屬性的對象的字符串表示形式。這些 choices 用於在可瀏覽的 API 中填充選擇的 HTML input。
要爲這些 input 提供自定義表示,請重寫 RelatedField
子類的 display_value()
方法。這個方法將接收一個模型對象,而且應該返回一個適合表示它的字符串。例如:
class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def display_value(self, instance):
return 'Track: %s' % (instance.title)
複製代碼
在渲染可瀏覽的 API 關係字段時,默認只顯示最多 1000 個可選 item。若是存在更多項目,則會顯示 "More than 1000 items…" 的 disabled 選項。
此行爲旨在防止因爲顯示大量關係而致使模板沒法在可接受的時間範圍內完成渲染。
有兩個關鍵字參數可用於控制此行爲:
html_cutoff
- 設置 HTML select 下拉菜單中顯示的選項的最大數量。設置爲 None
能夠禁用任何限制。默認爲 1000
。html_cutoff_text
- 設置一個文本字符串,在 HTML select 下拉菜單超出最大顯示數量時顯示。默認是 "More than {count} items…"
你還能夠在 settings 中用 HTML_SELECT_CUTOFF
和 HTML_SELECT_CUTOFF_TEXT
來全局控制這些設置。
在強制執行 cutoff 的狀況下,您可能但願改成在 HTML 表單中使用簡單的 input 字段。你可使用 style
關鍵字參數來作到這一點。例如:
assigned_to = serializers.SlugRelatedField(
queryset=User.objects.all(),
slug_field='username',
style={'base_template': 'input.html'}
)
複製代碼
請注意,反向關係不會自動包含在 ModelSerializer
和 HyperlinkedModelSerializer
類中。要包含反向關係,您必須明確將其添加到字段列表中。例如:
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ('tracks', ...)
複製代碼
一般須要確保已經在關係上設置了適當的 related_name
參數,能夠將其用做字段名稱。例如:
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
...
複製代碼
若是你尚未爲反向關係設置相關名稱,則須要在 fields
參數中使用自動生成的相關名稱。例如:
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ('track_set', ...)
複製代碼
若是要序列化通用外鍵,則須要自定義字段,以明確肯定如何序列化關係。
例如,給定一個如下模型的標籤,該標籤與其餘任意模型具備通用關係:
class TaggedItem(models.Model):
""" Tags arbitrary model instances using a generic relation. See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/ """
tag_name = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
tagged_object = GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag_name
複製代碼
如下兩種模式能夠用相關的標籤:
class Bookmark(models.Model):
""" A bookmark consists of a URL, and 0 or more descriptive tags. """
url = models.URLField()
tags = GenericRelation(TaggedItem)
class Note(models.Model):
""" A note consists of some text, and 0 or more descriptive tags. """
text = models.CharField(max_length=1000)
tags = GenericRelation(TaggedItem)
複製代碼
咱們能夠定義一個可用於序列化標籤實例的自定義字段,並使用每一個實例的類型來肯定它應該如何序列化。
class TaggedObjectRelatedField(serializers.RelatedField):
""" A custom field to use for the `tagged_object` generic relationship. """
def to_representation(self, value):
""" Serialize tagged objects to a simple textual representation. """
if isinstance(value, Bookmark):
return 'Bookmark: ' + value.url
elif isinstance(value, Note):
return 'Note: ' + value.text
raise Exception('Unexpected type of tagged object')
複製代碼
若是你須要的關係具備嵌套表示,則能夠在 .to_representation()
方法中使用所需的序列化類:
def to_representation(self, value):
""" Serialize bookmark instances using a bookmark serializer, and note instances using a note serializer. """
if isinstance(value, Bookmark):
serializer = BookmarkSerializer(value)
elif isinstance(value, Note):
serializer = NoteSerializer(value)
else:
raise Exception('Unexpected type of tagged object')
return serializer.data
複製代碼
請注意,使用 GenericRelation
字段表示的反向通用鍵可使用常規關係字段類型進行序列化,由於關係中目標的類型老是已知的。
默認狀況下,將指定帶有 through
模型的 ManyToManyField
的關係字段設置爲只讀。
若是你要明確指定一個指向具備 through 模型的 ManyToManyField
的關係字段,請確保將 read_only
設置爲 True
。