文章目录
这是对DRF官方文档:Serializer relations的翻译,根据个人的理解对内容做了些补充、修改和整理。
一,django模型间的关系
在我们对数据进行建模时,最重要的一点就是根据功能需求分析出实体及其关系,
在实现阶段:
- 一个实体对应一个模型,一个模型就是一张数据表;
- 实体间的关系由模型中的关系字段进行表示,模型间的关系就是数据表间的关系。
这种设计理念与关系型数据库的设计理念相符。而关系型数据库的强大之就处在于表示与处理各表之间的关联关系。
为此,Django 提供了定义三种最常见的数据库关联关系的方法的关系字段:
OneToOneField
:一对一关系ForeignKey
:多对一关系ManyToManyField
:多对多关系
二,检查序列化器实例详情
在使用 Serializer
类创建序列化器类时,对关系进行正确的的表示就非常重要。
而使用 ModelSerializer
类创建序列化器类时,它会自动使用合适的字段处理模型间的关系,检查这些自动生成的字段对于如何确定自定义关系来说是非常有帮助的。
例如查看一个ModelSerializer
子类的实例详情:
>>> 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())
三,序列化关系字段
为了解释各种类型的关系字段,官网的教程为我们提供了一些简单的模型:用于音乐专辑以及其中的歌曲。
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 __str__(self):
return '%d: %s' % (self.order, self.title)
模型中只用到多对一关系,Album 是主模型,产生主表,Track 是从模型,产生从表。
(一)StringRelatedField
字段作用:
主模型序列化器中的StringRelatedField
字段可以使用从模型中的__str__
方法来表示关联关系。
字段参数:
many
:如果应用于多对一的关系,应该设置为 True。
使用示例:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
效果就是 tracks 属性将使用 Track 模型的 __str__
方法来产生关联关系,并使用它返回的内容。
将产生以下形式的序列化结果:
{
'album_name': 'Things We Lost In The Fire',
'artist': 'Low',
'tracks': [
'1: Sunflower',
'2: Whitetail',
'3: Dinosaur Act',
...
]
}
(二)PrimaryKeyRelatedField
字段作用:
主模型序列化器中的PrimaryKeyRelatedField
字段可以使用从模型中的主键字段来表示关联关系。
- 每个模型必须有一个主键。要么手动使用
primary_key=True
设置某字段为主键,否则 django 会为表创建名为 id (别名 pk)的自动增长的字段作为主键。参考django doc:Automatic primary key fields
字段参数:
queryset
:验证字段输入时用于模型实例查找的查询集。关系字段必须显式设置queryset
或者read_only=True
。many
:如果应用于多对一的关系,应该设置为 True。allow_null
:是否允许该字段接受 None 或为空关系接受空字符串。默认值为 False。pk_field
:设置一个字段来控制主键值的序列化/反序列化。
使用示例:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
- 默认
PrimaryKeyRelatedField
字段是可读写的。可以使用read_only
标志来更改此行为。
效果就是 tracks 属性将使用 Track 模型的主键即 id (我们没在模型中指定主键)来产生关联关系,并使用主键值。
将产生以下形式的序列化结果:
{
'album_name': 'Undun',
'artist': 'The Roots',
'tracks': [
89,
90,
91,
...
]
}
(三)HyperlinkedRelatedField
字段作用:
主模型序列化器中的HyperlinkedRelatedField
可以用来表示使用超链接的关系目标。从模型中的主键来表示关联关系。
- 该字段是为映射到一个URL的对象设计的,该URL接受一个 URL 关键字参数,使用
lookup_field
和lookup_url_kwarg
参数设置。这适用于URL中包含一个主键或slug
参数的URL。如果需要更复杂的超链接表示,则需要自定义字段,请参考自定义超链接字段。
字段参数:
view_name
:应该用作关系目标的视图名称。如果使用标准的路由器类,则因该是一个格式为 modelname-detail 的字符串。是必要参数。queryset
:验证字段输入时用于模型实例查找的查询集。关系字段必须显式设置queryset
或者read_only=True
。many
:如果应用于多对一的关系,应该设置为 True。allow_null
:是否允许该字段接受 None 或为空关系接受空字符串。默认值为 False。lookup_field
:应该用于查找的字段。应该对应于被引用视图上的 URL 关键字参数。默认设置是 pk。lookup_url_kwarg
:在 URL 配置文件中定义的关键字参数的名称,该参数是应该用于查找字段。默认使用与lookup_field
相同的值。format
:如果使用格式后缀,超链接字段将为目标使用相同的格式后缀,除非使用format
参数重写。
使用示例:
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']
- 默认
HyperlinkedRelatedField
字段是可读写的。可以使用read_only
标志来更改此行为。
效果就是 tracks 属性将使用 Track 模型的主键即 id (我们没在模型中指定主键)来产生关联关系,并使用主键值。
将产生以下形式的序列化结果:
{
'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/',
...
]
}
(四)SlugRelatedField
字段作用:
主模型序列化器中的SlugRelatedField
字段可以使用从模型中的Slug
字段来表示关联关系。可以使用目标上的字段来表示关系的目标。
- django向模型提供Slug字段来创建更加完善的 URL。Rest Framework的
SlugRelatedField
字段也采用相同的理念。 -
django相关字段解释(slug)、在 Django 中生成 slug、django中slug是什么?有什么用?可以应用于什么地方?
字段参数:
slug_field
:从模型中用来表示它的字段。这应该是唯一标识任何给定实例的字段。是必要参数。当使用queryset
:验证字段输入时用于模型实例查找的查询集。关系字段必须显式设置queryset
或者read_only=True
。many
:如果应用于多对一的关系,应该设置为 True。allow_null
:是否允许该字段接受 None 或为空关系接受空字符串。默认值为 False。
使用示例:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='title'
)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
- 默认情况下,这个字段是可读写的。可以使用
read_only
标志来更改此行为。还必须确保模型中该字段设置了unique=True
。
效果就是 tracks 属性将使用 Track 模型的 title 字段来产生关联关系,并使用其值。
将产生以下形式的序列化结果:
{
'album_name': 'Dear John',
'artist': 'Loney Dear',
'tracks': [
'Airport Surroundings',
'Everything Turns to You',
'I Was Only Going Out',
...
]
}
(五)HyperlinkedIdentityField
字段作用:
主模型序列化器中的HyperlinkedIdentityField
字段可以作为身份关系应用,例如HyperlinkedModelSerializer
上的“url”字段。它也可以用于对象的属性。
字段参数:
view_name
:应该用作关系目标的视图名称。如果使用标准的路由器类,则因该是一个格式为 modelname-detail 的字符串。是必要参数。lookup_field
:应该用于查找的字段。应该对应于被引用视图上的 URL 关键字参数。默认设置是 pk。lookup_url_kwarg
:在 URL 配置文件中定义的关键字参数的名称,该参数是应该用于查找字段。默认使用与lookup_field
相同的值。format
:如果使用格式后缀,超链接字段将为目标使用相同的格式后缀,除非使用format
参数重写。
使用示例:
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')
class Meta:
model = Album
fields = ['album_name', 'artist', 'track_listing']
- 这个字段是只读写的。
效果就是 tracks 属性将使用 Track 模型的主键即 id (我们没在模型中指定主键)来产生关联关系,并使用主键值。
将产生以下形式的序列化结果:
{
'album_name': 'The Eraser',
'artist': 'Thom Yorke',
'track_listing': 'http://www.example.com/api/track_list/12/',
}
四,嵌套关系
与在序列化器中使用序列化关系字段专门序列化模型间关系的方式所不同的是,序列化器本身还能作为序列化字段来表示一种模型间的嵌套关系。例如:
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
参数(通常是一个模型实例),并应该返回所需要的序列化表示形式。
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)',
...
]
}
如果希望实现可读写的关系字段,则必须实现.to_internal_value(self, data)
方法。
如果希望提供基于上下文的动态queryset
,则需要重写.get_queryset(self)
,而不是在类上或初始化字段时指定.queryset
。
六,自定义超链接字段
如果希望自定义超链接字段的行为,以表示需要多个查询字段的 URL,则可以通过子类化 HyperlinkedRelatedField
来实现。有两个方法重需要写:
1, get_url(self, obj, view_name, request, format)
:用于将对象实例映射到其 URL 表示。
- 如果
view_name
和lookup_field
属性未配置为正确匹配 URL,可能会引发NoReverseMatch
。
2,get_object(self, queryset, view_name, view_args, view_kwargs)
:用于支持可写的超链接字段,以便将传入的 URL 映射回它们表示的对象。对于只读的超链接字段,无需重写此方法。
- 此方法的返回值应该是与匹配的 URL 参数对应的对象,可能会引发
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):
# 我们将它们定义为类属性,因此我们不需要将它们作为参数传递。
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 样式也是合理的。