Django REST Framework вложенный ключ ресурса "id" недоступен

Поэтому у меня есть следующая структура:

Клиентский файл принадлежит владельцу (имя класса = Контакт). Я пытаюсь создать клиентский файл с помощью API. Запрос содержит следующие данные:

{
  name: "Hello!"
  owner: {
    id: 1,
    first_name: "Charlie",
    last_name: "Watson"
  }
}

Я создал сериализатор в соответствии с моей структурой. Надеясь, что этот вызов API создаст клиентский файл с именем "Hello!" и контакт id 1 как владелец:

class ContactSerializer(serializers.ModelSerializer):
  class Meta:
    model = Contact
    fields = (
      'id',
      'first_name',
      'last_name',
    )

class ClientfileSerializer(serializers.ModelSerializer):

  owner = ContactSerializer(read_only=False)

  class Meta():
    model = Clientfile
    fields = (
      'id',
      'name',
      'owner',
    )

  def create(self, validated_data):

    owner = Contact.objects.get(pk=validated_data['owner']['id'])

Я вхожу в метод create. Однако единственное поле, которое мне нужно (['owner']['id']) недоступно. Если я print ['owner']['first_name'] он вернет "Charlie". Но идентификатор по некоторым причинам не кажется доступным...

Любые причины, по которым это может произойти? Я что-то упускаю? (Я новичок в Django)


РЕШЕНИЕ: Просто выяснилось, что причина, по которой идентификатор не показывалась в первую очередь, заключалась в том, что я должен был объявить ее в полях следующим образом: Надеюсь, это поможет.

class ContactSerializer(serializers.ModelSerializer):

  id = serializers.IntegerField() # ← Here

  class Meta:
    model = Contact
    fields = (
      'id',
      'first_name',
      'last_name',
    )

Ответ 1

Хорошо, поэтому я нашел другой подход, который работает. Я добавил сериализатор IntegerField для отношения владельца. Мне также пришлось установить отношение владельца к read_only = True.

Это json, который я отправляю через POST:

{
  name: "Hello!"
  owner_id: 1
}

Это мой сериализатор:

class ClientfileSerializer(serializers.ModelSerializer):

  owner_id = serializers.IntegerField()
  owner = ContactSerializer(read_only=True)

  class Meta():
    model = Clientfile
    fields = (
      'id',
      'owner_id',
      'owner',
    )

Это кажется менее крутым, чем первый способ, но он выполняет эту работу. Кроме того, я не хочу создавать нового владельца, но просто выберите тот, который уже находится в базе данных. Так что, возможно, это более семантический, чтобы иметь только идентификатор, а не полный набор информации, отправленный через Json.

Ответ 2

В полях Django REST Framework AutoField (автоматически создаваемые) по умолчанию используются только для чтения. Из документов:

read_only

Установите для этого значение " True чтобы гарантировать, что это поле используется при сериализации представления, но не используется при создании или обновлении экземпляра во время десериализации.

По умолчанию False

Вы можете увидеть это, проверив свой сериализатор, напечатав представление в своей оболочке:

serializer = ClientfileSerializer()
print repr(serializer)

Вы можете переопределить это, установив read_only=False в поле id в extra_kwargs:

class ContactSerializer(serializers.ModelSerializer):
  class Meta:
    model = Contact
    fields = (
      'id',
      'first_name',
      'last_name',
    )
    extra_kwargs = {'id': {'read_only': False}}

class ClientfileSerializer(serializers.ModelSerializer):

  owner = ContactSerializer(read_only=False)

  class Meta():
    model = Clientfile
    fields = (
      'id',
      'name',
      'owner',
    )
    extra_kwargs = {'id': {'read_only': False}}

Ответ 3

Вы можете попробовать что-то вроде этого:

class YourModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = YourModel
        fields = ('id', 'field1', 'field2')

    def to_internal_value(self, data):
        """
        Dict of native values <- Dict of primitive datatypes.
        Add instance key to values if 'id' present in primitive dict
        :param data:
        """
        obj = super(YourModelSerializer, self).to_internal_value(data)
        instance_id = data.get('id', None)
        if instance_id:
            obj['instance'] = YourModel.objects.get(id=instance_id)
        return obj

Затем в валидированных данных с сериализатором вы должны иметь ключ "instance", если request.data имеет ключ "id".

Также вы можете добавить только "id" вместо полного экземпляра/объекта.