Serializer relations - Django REST framework

龚浩宕
2023-12-01

序列化关系-Django REST框架

relations.py

串行化关系

数据结构,而不是算法,是编程的核心。

罗伯派克

关系字段用于表示模型关系。它们可以应用于ForeignKey, ManyToManyFieldOneToOneField关系,以及反转关系,以及自定义关系,例如GenericForeignKey.


注:关系字段声明为relations.py,但按照约定,您应该从serializers模块,使用from rest_framework import serializers并将字段称为serializers.<FieldName>.


检查关系。

当使用ModelSerializer类时,将自动为您生成序列化器字段和关系。检查这些自动生成的字段可以是确定如何自定义关系样式的有用工具。

为此,打开Django shell,使用python manage.py shell,然后导入序列化程序类,实例化它,并打印对象表示形式…。

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

API参考

为了解释各种类型的关系字段,我们将使用几个简单的模型作为示例。我们的模型将是为音乐专辑,和歌曲列出的每一张专辑。

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)

StringRelatedField

StringRelatedField可以使用它来表示关系的目标。__str__方法。

例如,下面的序列化程序。

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-如果应用于to-多关系,则应将此参数设置为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-如果应用于to-多关系,则应将此参数设置为True.
  • allow_null-如果设置为True,则该字段将接受None或用于可空关系的空字符串。默认为False.
  • pk_field-设置为控制主键值的序列化/反序列化的字段。例如,pk_field=UUIDField(format='hex')将UUID主键序列化为其紧凑的十六进制表示形式。

超链接相关场

超链接相关场可以使用超链接来表示关系的目标。

例如,以下序列化程序:

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_fieldlookup_url_kwarg争论。

这适用于包含一个主键或段塞参数作为URL一部分的URL。

如果需要更复杂的超链接表示,则需要自定义该字段,如自定义超链接字段部分,下面。


论点:

  • view_name-应用作关系目标的视图名称。如果你用标准路由器类这将是一个具有以下格式的字符串<modelname>-detail. 所需.
  • queryset-验证字段输入时用于模型实例查找的查询集。关系必须显式地设置查询集,或者设置read_only=True.
  • many-如果应用于to-多关系,则应将此参数设置为True.
  • allow_null-如果设置为True,则该字段将接受None或用于可空关系的空字符串。默认为False.
  • lookup_field-应用于查找的目标上的字段。应该对应于引用视图上的URL关键字参数。默认值是'pk'.
  • 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作为读-写字段,您通常希望确保段塞字段对应于模型字段。unique=True.

论点:

  • slug_field-目标上应用来代表它的领域。这应该是唯一标识任何给定实例的字段。例如,username. 所需
  • queryset-验证字段输入时用于模型实例查找的查询集。关系必须显式地设置查询集,或者设置read_only=True.
  • many-如果应用于to-多关系,则应将此参数设置为True.
  • allow_null-如果设置为True,则该字段将接受None或用于可空关系的空字符串。默认为False.

超级链接标识域

此字段可作为标识关系应用,如'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-应用作关系目标的视图名称。如果你用标准路由器类这将是一个具有以下格式的字符串<model_name>-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。

您可以通过重写超链接相关场...有两种方法可以被重写:

get_url(Self、obj、view_name、请求、格式)

这个get_url方法用于将对象实例映射到其URL表示形式。

可能引发NoReverseMatch如果view_namelookup_field属性未配置为正确匹配URL conf。

get_object(Self,view_name,view_args,view_kwargs)

如果您想支持可写超链接字段,那么还需要重写get_object,以便将传入的URL映射回它们表示的对象。对于只读超链接字段,不需要重写此方法。

如果对应于匹配的URL conf参数的对象,则此方法的返回值。

可以提出ObjectDoesNotExist例外。

假设我们有一个接受两个关键字参数的Customer对象的URL,如下所示:

/api/<organization_slug>/customers/<customer_pk>/

这不能用默认实现来表示,默认实现只接受一个查找字段。

在这种情况下,我们需要覆盖超链接相关场为了得到我们想要的行为:

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参数是唯一需要的可写Relationship字段,在这种情况下,它用于执行从原始用户输入映射到模型实例的模型实例查找。

在Version2.x中,序列化类可以有时自动确定queryset论辩如果 a ModelSerializer课堂被利用了。

这个行为现在被替换为使用显式queryset可写关系字段的参数。

这样做可以减少隐藏的“魔法”的数量。ModelSerializer提供,使字段的行为更加清晰,并确保在使用ModelSerializer快捷方式,或使用完全显式。Serializer上课。

自定义HTML显示

内建__str__方法将用于生成用于填充choices财产。这些选择用于填充可浏览API中的SELECT HTML输入。

若要为此类输入提供自定义表示,请重写display_value()一种RelatedField子类。此方法将接收模型对象,并应返回一个适合表示该对象的字符串。例如:

class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
    def display_value(self, instance):
        return 'Track: %s' % (instance.title)

选择字段截断

在可浏览API中呈现时,关系字段默认仅显示最多1000个可选择项。如果存在更多的项,则禁用选项“…超过1000个项”。将会显示。

此行为旨在防止模板由于显示大量关系而无法在可接受的时间范围内呈现。

有两个关键字参数可以用来控制这种行为:

  • html_cutoff-如果设置,这将是HTML选择下拉显示的最大选项数。设为None禁用任何限制。默认为1000.
  • html_cutoff_text-如果设置,这将显示一个文本指示,如果最大数量的项目已经截止在一个HTML选择下拉。默认为"More than {count} items…"

您还可以使用以下设置全局控制这些HTML_SELECT_CUTOFFHTML_SELECT_CUTOFF_TEXT.

在执行截止点的情况下,您可能需要在HTML表单中使用一个普通的输入字段。您可以使用style关键字参数例如:

assigned_to = serializers.SlugRelatedField(
   queryset=User.objects.all(),
   slug_field='username',
   style={'base_template': 'input.html'}
)

反向关系

注意,反向关系不会自动包含在ModelSerializerHyperlinkedModelSerializer上课。若要包含反向关系,必须将其显式添加到字段列表中。例如:

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', ...]

参见Django关于反向关系更多细节。

一般关系

如果要序列化一般外键,则需要定义自定义字段,以显式确定如何序列化关系的目标。

例如,给定标记的以下模型,该模型与其他任意模型具有泛型关系:

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 __str__(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字段,可以使用常规关系字段类型序列化,因为关系中的目标类型总是已知的。

有关更多信息,请参见关于一般关系的Django文件.

具有直通式模型的ManyToManyfield

默认情况下,以ManyToManyField带着through指定的模型设置为只读。

如果显式指定指向ManyToManyField使用直通式模型时,一定要设置read_onlyTrue.

如果你想代表直通式模型上的额外字段然后,您可以将直通模型序列化为嵌套对象.


第三方包

下面的第三方包也是可用的。

DRF嵌套路由器

这个DRF-嵌套路由器包提供用于处理嵌套资源的路由器和关系字段。

REST框架通用关系

这个REST-框架-泛型-关系库为一般外键提供读/写序列化。

 类似资料:

相关阅读

相关文章

相关问答