【djangorestframework】15、Serializer relations(序列化器关系)

序列化关系(Serializer relations)

  • 糟糕的程序员担心代码。好的程序员担心数据结构和他们的关系。--Liuns Torvalds
  • 关系字段用于表示模型关系。它们可以应用于ForeignKey, ManyToManyField和OneToOneField关系,以及反向关系和自定义关系(例如:GenericForeignKey)
  • 注意:关系字段在relations.py中声明,但按照惯例,你应该从serializers模块导入它们,使用from rest_framework import serializers并像serializers.<FieldName>这样引用字段。
检查关系(Inspecting relationships)
  • 当使用ModelSerializer类时,将为你自动生成序列化器字段和关系。检查这些自动生成的字段可以作为确定如何定制关系样式的有用工具。
  • 为此,使用python manage.py shell打开Django 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())

API参考(API Reference)

  • 为了解释各种类型的关系字段,我们将为我们的示例使用一些简单的模型。我们的模型将用于音乐专辑,以及每个专辑中列出的歌曲。
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

  • StringRelatedField可用它的__unicode__方法来表示关系的目标。
  • 例如,如下序列化器。
class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.StringRelatedField(many=True)

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')
  • 将序列化为如下表示。
{
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': [
        '1: Sunflower',
        '2: Whitetail',
        '3: Dinosaur Act',
        ...
    ]
}
  • 该字段是只读的。
  • 参数:
    • many-如果是一对多的关系,就将此参数设置为True

PrimaryKeyRelatedField

  • 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

  • HyperlinkedRealtedField可用于使用超链接表示关系的目标。
  • 例如,以下序列化器:
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
    • lookup_field--用于查找的目标字段。对应于引用视图上的URL关键字参数。默认是'pk'
    • allow_null--如果设置为True,那么该字段将接受None值或可为空的空字符串。默认为False
    • lookup_url_kwarg--与查找字段对应的URL conf中定义的关键字参数的名称。默认使用与lookup_field相同的值
    • format--如果使用格式后缀,则超链接字段将使用与目标相同的格式后缀,除非使用format参数重写。

SlugRelatedField

  • 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.

HyperlinkedIdentityField

  • 此字段可以作为标识关系应用,例如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参数重写。

嵌套关系(Nested relatiuonships)

  • 可以使用序列化器作为字段来表示嵌套关系
  • 如果该字段用于表示一对多关系,则应将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},
        ...
    ],
}

可写的嵌套序列化器(Writable nested serializers)

  • 默认情况下,嵌套序列化器是只读的。如果要支持对嵌套序列化器的写操作,则需要创建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>

自定义关系字段(Custom relational fields)

  • 在极少数情况下,现有的关系样式都不适合你需要的表示,你可以实现一个完全自定义的关系字段,该字段准确地描述应该如何从模型实例生成输出表示。
  • 要实现自定义关系字段,你应该重写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)',
        ...
    ]
}

自定义超链接字段(Custom hyperlinked fields)

  • 在某些情况下,你可能需要自定义超链接字段的行为,以表示需要多个查询字段的URL
  • 你可以通过重写HyperlinkedRelatedField来实现,有两个方法重写
    • get_url(self, obj, views_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异常

posted @ 2022-04-25 16:12  郭祺迦  阅读(74)  评论(0)    收藏  举报