Django REST Framework 嵌套资源键 "id" 无法访问

Django REST Framework nested resource key "id" unaccessible

所以我有以下结构:

ClientFile 属于所有者(class 姓名 = 联系人)。 我正在尝试使用 API 创建一个 Clientfile。该请求包含以下数据:

{
  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'])

我确实进入了创建方法。但是,我需要的唯一字段 (['owner']['id']) 无法访问。如果我做 print ['owner']['first_name'] 它会做 return 'Charlie'。但是由于某些原因,ID似乎无法访问...

为什么会发生这种情况?我错过了什么吗? (我是 Django 的新手)


解决方案:刚刚发现 ID 最初没有显示的原因是因为我必须像这样在字段中声明它:希望这有帮助。

class ContactSerializer(serializers.ModelSerializer):

  id = serializers.IntegerField() # ← Here

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

在 Django REST Framework AutoField 字段(自动生成的字段)默认为只读。来自 the docs:

read_only

Set this to True to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.

Defaults to False

您可以通过 inspecting your serializer 通过在您的 shell 中打印表示来查看此内容:

serializer = ClientfileSerializer()
print repr(serializer)

您可以通过针对 extra_kwargs 中的 id 字段设置 read_only=False 来覆盖它:

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

好的,所以我找到了一种不同的方法。 我为所有者关系添加了一个 IntegerField 序列化程序。我还必须将所有者关系设置为 read_only=True.

这是我通过 POST 发送的 json:

{
  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',
    )

它似乎不如第一种方式酷,但它确实起作用了。 另外,我不想创建一个新的所有者,而只是 select 一个已经在数据库中的所有者。因此,仅具有 ID 而不是通过 Json.

发布的全套信息可能更具语义性

您可以尝试这样的操作:

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

然后在序列化程序验证的数据中,如果 request.data 有 "id" 键,你应该有 "instance" 键。

您也可以只添加 "id" 而不是完整的 instance/object。

投票最高的答案确实解决了问题,但它引发了一个新问题,如评论中所述,我们无法再创建新记录,因为它会抛出 ac 异常,说明需要。我们可以将 id 设置为 required=False 然后 id 将在 validated_data 中可用,并且不需要手动设置它

id = serializers.IntegerField(required=False) <- Like this

class Meta:
    model = Details
    fields = ('id', 'product_name', 'description', 'specification', 'make_model',
              'brand', 'quantity',)