Django外键访问是如何工作的
假设我有一个这样的模型。
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?
我查了一下源代码,但没找到我想要的答案。
3 个回答
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
和 client_id
(单下划线)。
client_id
是一个普通的(整数)属性。它是保存到数据库中的外键。即使你指定了 ForeignKey
为 client
,在数据库中你只会看到 client_id
这一列。
client
属性是一个对象描述符实例。它是一个特殊的类,重写了 __get__
和 __set__
方法,所以设置和访问这个属性时会调用这个类的方法。这就是让你能够访问实际相关模型实例的“魔法”。如果 client_id
属性对应的模型实例还没有加载,__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
语句,从而提高了性能。
这个在文档里有解释:
https://docs.djangoproject.com/en/dev/ref/models/fields/#database-representation
在数据库里,只有一个叫 client_id
的字段(注意是一个下划线)。
在模型实例中,你会看到一个 client
属性,当你访问这个属性时,Django 会从数据库中加载相关的对象,并把它当作另一个模型实例来使用。
你还会有一个 client_id
属性(注意是一个下划线),它存储的是相关对象的主键值,也就是在数据库字段中保存的值。
在进行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)