Руководство по API среды Django REST (11): Сериализация · Отношения

задняя часть Python Django

Официальная оригинальная ссылка
Эта серия статей адрес github
Пожалуйста, укажите источник

Связь сериализатора

Поля отношений используются для представления отношений модели. они могут быть применены кForeignKey,ManyToManyFieldиOneToOneFieldотношения, обратные отношения иGenericForeignKeyи другие пользовательские отношения.

Уведомление:Поле отношений находится вrelations.pyзаявление, но по соглашению вы должны начать сserializersмодули импортируют их, используяfrom rest_framework import serializersимпортировать и т.п.serializers.<FieldName>Поля ссылаются следующим образом.

Проверьте отношения.

в настоящее время используетModelSerializerкласс, сериализованные поля и отношения автоматически генерируются для вас. Просмотрите эти автоматически сгенерированные поля, чтобы узнать, как настроить стиль отношений.

Для этого используйтеpython manage.py shellОткройте оболочку Django, затем импортируйте класс сериализации, создайте его экземпляр и распечатайте представление объекта...

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

Чтобы объяснить различные типы реляционных полей, мы будем использовать несколько простых моделей для нашего примера. В нашей модели будут использоваться музыкальные альбомы и треки, перечисленные в каждом альбоме.

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Значение или пустая строка для отношения, допускающего значение NULL. По умолчаниюFalse.
  • pk_field- Установить как поле для управления сериализацией/десериализацией значений первичного ключа. Например,pk_field=UUIDField(format='hex')Первичный ключ UUID сериализуется в его компактное шестнадцатеричное представление.

HyperlinkedRelatedField

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Объект настройки параметров.

Это работает для URL-адресов, которые содержат один первичный ключ или параметр slug как часть URL-адреса.

Если вам нужно более сложное представление гиперссылки, вам нужно настроить это поле, что будет объяснено позже.


параметр:

  • view_name- Имя представления для использования в качестве цели отношения. Если вы используете стандартный класс маршрутизатора, это будет формат<modelname>-detailНить.необходимые.
  • queryset- Набор запросов для использования в запросах экземпляра модели при проверке ввода в поле. Набор запросов должен быть установлен явно или установленread_only=True.
  • many- Если применяется к отношению "один ко многим", этот параметр должен быть установлен вTrue.
  • allow_null- если установленоTrue, то поле приметNoneЗначение или пустая строка для отношения, допускающего значение NULL. По умолчаниюFalse.
  • lookup_field- Целевое поле для поиска. Соответствует аргументам ключевого слова URL в представлении ссылки. По умолчанию'pk'.
  • lookup_url_kwarg- Имя аргумента ключевого слова, определенного в конфигурации URL, соответствующей полю поиска. По умолчанию используется с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Значение или пустая строка для отношения, допускающего значение NULL. По умолчанию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, соответствующей полю поиска. По умолчанию используется с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, могутNoReverseMatch.

get_object(self, queryset, view_name, view_args, view_kwargs)

Если вы хотите поддерживать доступные для записи поля гиперссылок, вам также необходимо переопределитьget_object, чтобы сопоставить входящие URL-адреса с объектами, которые они представляют. Для полей гиперссылок, доступных только для чтения, этот метод не нужно переопределять.

Возвращаемым значением этого метода должен быть объект, соответствующий соответствующему параметру конфигурации URL.

может привести кObjectDoesNotExistаномальный.

взять каштан

Предположим, у нас есть 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Преобразование между классами тривиально.

пользовательское отображение HTML

модель встроенная__str__метод, используемый для созданияchoicesСтроковое представление объекта свойства. Эти варианты используются для заполнения выбранных входных данных HTML в доступном для просмотра API.

Чтобы предоставить пользовательские представления для этих входных данных, переопределитеRelatedFieldподклассdisplay_value()метод. Этот метод получит объект модели и должен вернуть строку, подходящую для его представления. Например:

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

Select field cutoffs

При отображении просматриваемых полей отношений API по умолчанию отображается не более 1000 необязательных элементов. Если элементов больше, отображается отключенная опция «Более 1000 элементов…».

Это поведение предназначено для предотвращения отображения шаблона в приемлемые сроки из-за большого количества отображаемых взаимосвязей.

Есть два аргумента ключевого слова, которые можно использовать для управления этим поведением:

  • html_cutoff- Устанавливает максимальное количество параметров, отображаемых в раскрывающемся списке выбора HTML. Установить какNoneЛюбое ограничение можно отключить. По умолчанию1000.
  • html_cutoff_text- Устанавливает текстовую строку для отображения, когда раскрывающийся список выбора HTML превышает максимальный размер экрана. По умолчанию"More than {count} items…"

Вы также можете использовать в настройкахHTML_SELECT_CUTOFFиHTML_SELECT_CUTOFF_TEXTдля глобального управления этими настройками.

В случаях, когда применяется отсечение, вместо этого вы можете использовать простые поля ввода в HTML-формах. ты можешь использовать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Обратный универсальный ключ, представленный полем, может быть сериализован с использованием обычного реляционного типа поля, поскольку тип цели в отношении всегда известен.

ManyToManyFields со сквозной моделью

По умолчанию он будет указан сthroughмодельManyToManyFieldПоле отношения доступно только для чтения.

Если бы вы явно указали указатель на модель со сквознымManyToManyFieldполя отношения, обязательно установитеread_onlyУстановить какTrue.