【问题标题】:Django Rest Framework - Updating a foreign keyDjango Rest Framework - 更新外键
【发布时间】:2016-01-09 16:53:33
【问题描述】:

我对使用 Django Rest Framework 的这个问题感到有点沮丧:

我正在使用带有custom serializer视图集。这个序列化器有它的depth set to 1。当我查询这个视图集时,我得到了正确的数据表示,例如:

data = {
  id: 1,
  issue_name: 'This is a problem',
  status: {
    id: 3,
    name: 'todo'
  }
}

问题在我需要更新状态时出现。例如,如果我想为此问题选择其他状态,例如:

status_new = {
   id: 4,
   name: 'done'
}

我将以下 PATCH 发送回服务器,这是输出:

data = {
  id: 1,
  issue_name: 'This is a problem',
  status: {
    id: 4,
    name: 'done'
  }

}

但是,状态不会更新。事实上,它甚至不是validated_data 字典的一部分。我读过嵌套关系是只读的。有人可以用简单的方式告诉我我需要做什么吗?

真的很感激。

提前致谢

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    documentation 中所述,您需要在序列化程序中编写自己的create()update() 方法以支持可写嵌套数据。

    您还需要显式添加状态字段,而不是使用 depth 参数,否则我相信它不会自动添加到 validated_data

    编辑:也许我对细节有点缺乏:你想要做的是覆盖 ModelIssueSerializer 中的更新。这基本上会在序列化器级别拦截 PATCH/PUT 请求。然后获取新状态并将其分配给实例,如下所示:

    class StatusSerializer(serializers.ModelSerializer):
        class Meta:
            model = Status
    
    class ModelIssueSerializer(serializers.ModelSerializer):
        status = StatusSerializer()
        # ...
        def update(self, instance, validated_data):
            status = validated_data.pop('status')
            instance.status_id = status.id
            # ... plus any other fields you may want to update
            return instance
    

    我在评论中提到您可能需要添加StatusSerializer 字段的原因是为了将状态输入validated_data。如果我没记错的话,如果你只使用depth,那么嵌套对象可能不会在update()/create() 方法中被序列化(尽管我可能会弄错)。无论如何,添加StatusSerializer 字段只是使用depth=1 的显式形式

    【讨论】:

    • 如果我不使用深度,那么我将如何获得完整的结构,例如:status: { id: 4, name: 'done' }。您能否给我或指出一个示例或资源来进行您所说的更新?
    • 只需为 Status 定义一个 ModelSerializer 并将其作为字段包含在您的其他序列化程序中,例如status = StatusSerializer()。至于update() 的示例,DRF 的文档中有一些(请参阅我的答案中的链接)
    • 我确实尝试过,但是为状态字段定义序列化程序会导致状态记录自行更新,这不是我想要的。让我举个例子:class ModeIIssue(models): name = models.CharField(max_length=30) status = ForeignKey(ModelStatus) 我想要更新状态字段的引用而不是状态记录本身。
    • 查看我的更新答案。只是一小部分 DRF 建议:在这种情况下尽量避免使用可写的 ModelSerializer。当模型和 API 端点之间存在简单直接的关系(例如,没有可修改关系的具体模型)时,可写 ModelSerializer 很有用。对于嵌套的可写字段来说更是如此:唯一有意义的情况是完全包含的模型。在您的情况下,将问题详细 URL 上的 PATCH/PUT 动词路由到完成这项工作的自定义视图可能是一个更好的主意。简单得多,甚至不会破坏 RESTful。
    • 如何在自定义 update() 中为多对多关系更新实例的值。我正在为多对多关系使用中间模型,因此不允许使用 instance.relationship.add() 方法。 docs.djangoproject.com/en/1.8/topics/db/models/…
    【解决方案2】:

    我通常在这种情况下使用自定义字段。

    class StatusField(serializers.Field):
    
        def to_representation(self, value):
            return StatusSerializer(value).data
    
        def to_internal_value(self, data):
            try:
                return Status.objects.filter(id=data['id']).first()
            except (AttributeError, KeyError):
                pass
    
    

    然后在主序列化器中:

    class IssueSerializer(serializers.ModelSerializer):
        status = StatusField()
    
        class Meta:
            model = MyIssueModel
            fields = (
                'issue_name',
                'status',
            )
    

    【讨论】:

      【解决方案3】:

      我会假设您的模型模仿了您的序列化程序的数据。另外,我会假设您与状态有一对多的关系,但您不需要通过问题序列化程序创建它们,您有一个不同的端点。在这种情况下,您可能会得到SlugRelatedField

      from rest_framework import serializers
      
      
      class StatusSerializer(serializers.ModelSerializer):
          class Meta:
              model = MyStatusModel
              fields = (
                  'id',
                  'status',
              )
      
      
      class IssueSerializer(serializers.ModelSerializer):
          status = serializers.SlugRelatedField(slug_field='status', queryset=MyStatusModel.objects.all())
      
          class Meta:
              model = MyIssueModel
              fields = (
                  'issue_name',
                  'status',
              )
      

      另一个有效的解决方案是在此处保留外键值并通过 ui-select 或 select2 组件处理前端的显示名称 - RESTfull 方法:您正在处理引用状态的问题对象对象。在 Angular 前端应用程序中,您将在特定路由上从后端查询所有状态,然后您将根据外键值表单问题显示正确的描述性名称。

      让我知道这对你有什么影响。

      【讨论】:

      • 背后的想法是我有一个问题条目,它有一个状态。状态可以是新的、进行中、已解决等。从前端我可以选择另一个状态,例如已解决,然后点击保存。我想要的是当我点击保存问题时应该通过 ajax 更新。此流程对于问题模型的其他字段运行良好,但不会更新状态字段。我会添加 update 代码,但字段 status 不会出现在 valid_data 字典中。
      • 状态列表是“固定的”还是在不同位置的应用程序中可配置?你只需要在issue中参考这里的状态,对吧?
      • 是的,状态可以在应用程序的其他地方配置,我需要在这里参考和更改问题的状态。
      猜你喜欢
      • 2017-08-15
      • 2013-10-24
      • 2017-04-14
      • 2014-03-24
      • 2019-07-02
      • 2015-03-27
      • 2021-06-25
      • 1970-01-01
      • 2019-07-27
      相关资源
      最近更新 更多