Django ORM 不会从保存的对象刷新外键

Posted

技术标签:

【中文标题】Django ORM 不会从保存的对象刷新外键【英文标题】:Django ORM does not refresh foreign key from saved object 【发布时间】:2017-03-17 06:22:32 【问题描述】:

更新对象中的外键后,相关对象不会刷新。

模型.py:

class Name(models.Model):
    name = models.CharField(max_length=30, unique=True)
    status = models.ForeignKey( 'NameStatus' )

class NameStatus(models.Model):
    status = models.CharField(max_length=30, unique=True)

测试它:

     >>> name_obj = Name.objects.exclude(name="Foo").get()
     >>> print ( status.status.id )
     2
     >>> name_obj.status_id = status.status.id + 1
     >>> print ( "%d vs %d" % (status.status.id, status.status_id )
     2 vs 3

样本数据:

姓名:

| id | name | status (status_id) |
----------------------------------
| 1  | Foo  | 2                  |
| 2  | Bar  | 3                  |

状态:

| id | status        |
----------------------
| 1  | First Status  |
| 2  | Second Status |
| 3  | Third Status  |

我错过了什么?

是否应该在刷新对象的查询设置中再次调用 .get()?

我知道这可能是为了减少对数据库的不必要调用,但是如何避免呢?

有了这个示例数据,我如何在 Django orm 中将外键从 Second Status 移动到 Third Status

PS:正如下面的一些答案试图争辩说 field_id 属性不存在或者它会创建一个新的对象属性。这个不对。它在调试器下进行了测试,并且 save() 确实在数据库中反映了它。 _id 文档参考为:https://docs.djangoproject.com/en/1.10/ref/models/fields/#database-representation

编辑:将状态对象更改为名称以反映类权限。添加了示例数据。

【问题讨论】:

为什么要添加一个新的键 status_id 来命名对象(因为模型中没有定义相同的字段)? 因为 Django 是这样命名数据库中 Name 表中的 status 字段的:status_id。这是处理它的错误方式(tm)吗? 首先你的对象命名约定有问题。 已按照您的建议进行编辑以反映对象名称。 【参考方案1】:

重写你的测试语句:

 >>> name_obj = Name.objects.exclude(name="Foo").get()
 >>> print ( name_obj.status.id )
 2
 >>> name_obj.status.id = name_obj.status.id + 1
 >>> print ( "%d vs %d" % (name_obj.status.id, name_obj.id)

预期答案:3 对 2

【讨论】:

name_obj.id 和 name_obj.status_id 是不同的字段。 但是在 django orm 中你是如何读取外键的。不要与实际的 db 列混淆。上面的就可以了,试一试吧。 添加了一些示例数据并按照您的建议重命名了对象变量以使其清晰。 它仍然与我的答案不同。和我的一样。 完成。不过,name_obj.status.id 和 name_obj.id 是不同表的 pk。【参考方案2】:

我相信你在这里做了两件不同的事情。

使用 status.status.id,您可以处理对象的数据库 ID。 使用 status.status_id,您可以“即时”创建一个新的对象属性 status_id 并将值 3 分配给它

前者与后者无关,所以它们可以有不同的值

例如,你可以这样做:

from .models import Name

status = Name.objects.exclude(name="Foo").get()
status.status.foo = 'bar' 

结果如下:status.status.foo 会给你'bar'。

【讨论】:

你确定吗?据我了解, status.status.id 是状态表的 pk 而不是名称表的 pk。使用这个逻辑,对象的数据库 id 就是 status.id。 是的,status.status.id是状态表的pk。 status.status_id 是 Name 对象的一个​​属性(在此示例中,它也被误导性地称为 status)。如果从查询集中检索的对象(它是一个名称对象)将被称为名称会更清楚。然后 name.status.id 为您提供相关状态对象的 id (pk),name.status_id 是在 name 对象上动态定义的属性。 修正了名称。关于_id,你错了:docs.djangoproject.com/en/1.10/ref/models/fields/… 我查过了。而且我仍然相信我写的是正确的:您可以使用 status_id 来处理值,但不能分配它。相反,我相信在为 name_obj.status_id 赋值时,会在 name_obj 上创建一个名为 status_id 的新属性,而不是在相关的状态 obj 上。我复制了它并得到了与您一样的确切保存结果:name_obj.status.id 给了我 2,而 name_obj.status_id 给了我 3。 我查过了。我相信在为 name_obj.status_id 赋值时,我们在 name_obj 实例上定义了一个新的实例属性。这会覆盖具有相同名称的类属性。

以上是关于Django ORM 不会从保存的对象刷新外键的主要内容,如果未能解决你的问题,请参考以下文章

Django_数据库——ORM对象操作

为啥 django ORM 的`save` 方法不返回保存的对象?

Django之 数据库ORM

Django通过多对多ORM对象中的多个标签进行过滤

Django orm总结

深入Django ORM的继承关系