[Solved]-How does Django foreign key access work

10👍

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)

11👍

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.

👤knbk

3👍

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

Leave a comment