Django 外键访问是如何工作的

Posted

技术标签:

【中文标题】Django 外键访问是如何工作的【英文标题】:How does Django foreign key access work 【发布时间】:2014-09-13 13:19:23 【问题描述】:

假设我有一个这样的模型。

class Job(models.Model):
    client = models.ForeignKey(Contacts, null=True)

假设我有工作 j。我知道我可以像这样访问属于j的客户端

 j.client

但也有

 j.client_id

所以我的问题是访问 j.client 是如何工作的?

django 是否存储 client__id 然后当调用 j.client 时它会查询以找到正确的对象?

或者是对象引用存储到 j 并且访问 client__id 是从 Contact 对象获取 id?

我浏览了一下源代码,但找不到我的问题的答案

【问题讨论】:

When does Django look up the primary key of foreign keys? 的可能重复项 【参考方案1】:

j.client 为您提供models.Model 对象。您可以访问它的属性,例如 ...

client = j.client

id = client.id
name = client.name

但不应该有j.client__id 字段。您应该使用j.client.id 来获取id 字段。虽然您可以使用j.client__id 字段进行过滤等。

所以,

id = j.client.id # good
id = j.client__id # bad

job = Job.objects.get(client__id=1) # good
job = Job.objects.get(client.id=1) # bad

【讨论】:

对不起我不是故意放多余的_有一个client_id字段你自己试试 来自 Django 的文档 - “在幕后,Django 将“_id”附加到字段名称以创建其数据库列名称。”。 我不确定为什么有人会根据输入错误的问题 -1 我的答案。 您是否因为有人对您的答案投了反对票而对其他两个答案都投了反对票? 我赞成这个以抵消反对票,因为这是我的错误。如果您对另外两个投了反对票,请撤消该操作。他们是很好的答案【参考方案2】:

这在文档中有解释:https://docs.djangoproject.com/en/dev/ref/models/fields/#database-representation

数据库中只有client_id字段(单下划线)

在模型实例上,您将拥有client 属性,当您访问它时,这将导致 Django 从数据库加载相关对象并实例化为另一个模型实例。

您还将拥有client_id 属性(一个下划线),该属性具有相关对象的主键值,存储在 db 字段中。

在进行 ORM 查询时,您可以使用 client__id(双下划线)语法来查找相关模型上的字段,例如,如果 Client 模型有 name 字段,您也可以使用 client__name。这将成为跨两个模型的 SQL JOIN 查询。

例如

Job.objects.get(client__id=1)
Job.objects.filter(client__name='John')

client = Client.objects.get(pk=1)
Job.objects.get(client=client)

【讨论】:

是的,我注意到我的错误并编辑了问题以正确反映访问权限。太棒了,这是我正在寻找的确认。我怀疑是这种情况,谢谢伙计!【参考方案3】:

您所说的可能是clientclient_id(单下划线)。

client_id 属性是常规(整数)属性。这是保存到数据库的外键。即使您将ForeignKey 指定为client,您也只会在数据库中看到client_id 列。

client 属性是一个对象描述符实例。它是一个特殊的类,它覆盖了__get____set__ 方法,因此设置和访问该属性会调用该类的方法。这是让您访问实际相关模型实例的魔力。如果尚未加载,__get__ 将根据client_id 属性从数据库中检索正确的模型实例。 __set__ 还将client_id 属性设置为相关对象的主键,这样client_id 始终是最新的。

请注意,此属性在查询查找中也可用,并且非常方便。例如,如果您只有一个外来对象的主键,而不是模型实例本身,则以下查询看起来非常相似:

job = Job.objects.filter(client__id=pk)
job = Job.objects.filter(client_id=pk)

但是,在第一个查询下,访问相关对象的属性(双下划线)并执行OUTER JOIN。第二个查询只访问本地属性,因此不必执行OUTER JOIN 语句并节省性能。

【讨论】:

是的,我已经改变了我的问题,在我发布它一分钟后就注意到了这个错误。你们对我来说太快了>.

以上是关于Django 外键访问是如何工作的的主要内容,如果未能解决你的问题,请参考以下文章

父模型具有多个外键时的Django外键反向访问[重复]

Django - 从 Django 模板访问外键管理器

Django 访问值对象中的反向外键数据

在 django admin 中使用外键访问另一个模型字段

通过django模板中的外键关系快速访问模型属性

在 Django 中直接访问外键 ID 字段是不是更快?