Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Django foreign key access work

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

like image 891
Charles Haro Avatar asked Jul 22 '14 15:07

Charles Haro


People also ask

How does ForeignKey work in Django?

ForeignKey is a Django ORM field-to-column mapping for creating and working with relationships between tables in relational databases. ForeignKey is defined within the django. db. models.

Does Django index foreign keys?

Django automatically creates an index for all models. ForeignKey columns.

How do I restrict foreign keys choices to related objects only in Django?

Update: ForeignKey. limit_choices_to allows to specify either a constant, a callable or a Q object to restrict the allowable choices for the key.


2 Answers

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.

like image 74
knbk Avatar answered Oct 17 '22 09:10

knbk


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)
like image 10
Anentropic Avatar answered Oct 17 '22 10:10

Anentropic