Reputation: 1886
Say I have a model like this.
class Job(models.Model):
client = models.ForeignKey(Contacts, null=True)
and lets say I have job j. I know I can access the client belonging to j like this
j.client
but there is also
j.client_id
So my question is how does accessing j.client work?
Does django store client__id then when j.client is called it does a query to find the correct object?
Or is the object reference stored to j and accessing client__id is getting the id from the Contact object?
I've looked around the source code a bit but couldn't find the answer to my question
Upvotes: 12
Views: 12987
Reputation: 33823
This is explained in the docs:
https://docs.djangoproject.com/en/dev/ref/models/fields/#database-representation
In the database there is only client_id
field (single underscore)
On the model instance you will have client
attribute, when you access it this will cause Django to load the related object from the db and instantiate as another model instance.
You will also have client_id
attribute (one underscore) which has the primary key value of the related object, as stored in the db field.
When doing ORM queries you are able to use client__id
(double underscore) syntax to lookup against fields on the related model, eg you could also do client__name
if Client
model had a name
field. This will become a SQL JOIN query across both models.
eg
Job.objects.get(client__id=1)
Job.objects.filter(client__name='John')
client = Client.objects.get(pk=1)
Job.objects.get(client=client)
Upvotes: 10
Reputation: 53649
What you are probably talking about is client
and client_id
(single underscore).
The client_id
attribute is a regular (integer) attribute. This is the foreign key that is saved to the database. You will only ever see a client_id
column in the database, even though you specify the ForeignKey
as client
.
The client
attribute is an object descriptor instance. It is a special class that overrides the __get__
and __set__
methods, so settings and accessing that attributes invokes that class's methods. This is the magic that gives you access to the actual related model instance. __get__
will retrieve the correct model instance from the database if it isn't loaded already, based on the client_id
attribute. __set__
will also set the client_id
attribute to the primary key of the related object, so that client_id
is always up-to-date.
Note that this attribute is also available in query lookups, and is quite handy. E.g., if you have just the primary key of a foreign object, and not the model instance itself, the following queries look very similar:
job = Job.objects.filter(client__id=pk)
job = Job.objects.filter(client_id=pk)
However, underneath the first query accesses an attribute on the related object (double underscore) and performs an OUTER JOIN
. The second query only ever accesses a local attribute, thus not having to perform the OUTER JOIN
statement and saving performance.
Upvotes: 11
Reputation:
j.client
gives you the models.Model
object. You can access it's properties like ...
client = j.client
id = client.id
name = client.name
But there should not be a j.client__id
field. You should use j.client.id
to get the id
field. Although you can use j.client__id
field to do filters and such.
So,
id = j.client.id # good
id = j.client__id # bad
and
job = Job.objects.get(client__id=1) # good
job = Job.objects.get(client.id=1) # bad
Upvotes: 3