How Django ORM retrieves reverse relation objects without making a query to Database during call?

Hello!

So to test out if django reverse relation makes a DB query I did the following steps.

I reset the queries

reset_queries()

Then I call a reverse relationship object

client.messenger_user

I check the queries and it’s empty

connection.queries
[]

I don’t share the models, because in this context it doesn’t matter. What matters is to have a reverse relation.

So my question is when does the Django ORM retrieve the object from DB?

And as it doesn’t make a DB query on the call part of reverse relation can I assume that making a query on the reverse related object model is heavier for DB?

i.e.

MessengerUser.objects.filter(client_id=client_id).exists()

hi, django uses caching mechanisms to avoid unnecessary queries. here are some relevant links 1 2 3

1 Like

Querysets are lazy.

Defining a queryset does not cause that queryset to be executed. You need to do something with the queryset to force it to be resolved before an actual reference to the DB will occur.

So this statement alone:

messenger_users = MessengerUser.objects.filter(client_id=client_id)

would not access the database. You wouldn’t see an entry in connection.queries.

(Without seeing exactly what client and messenger_user are, I can’t address the behavior you’re describing.)

1 Like

Thanks for you answer, Ken. Your answers to other users’ questions helped me a lot. You’re such a legend!

I don’t think I am allowed to share the models but the structure is as follows:

I have Client model. Also I have a MessengerUser which is related to Client with OneToOne relation.

I just realized that my study was incorrect, because I was doing this in Python Console, so it always prints the result of the queries, thus the query always executes(the laziness doesn’t work here).

I repeated the study in a file environment and in both cases I got database hits.

from django.db import reset_queries, connection
from loyalmed.apps.user.models import Client, MessengerUser

client = Client.objects.get(id=7410)

reset_queries()

messenger_user = client.messenger_user

print(f"After reverse_relation: {connection.queries = }")

reset_queries()

exists = MessengerUser.objects.filter(client_id=client.id).exists()

print(f"After Direct filtering: {connection.queries = }")

Query is lazy only when I don’t user the “exists()”.

So I had this question, because I have to check if Client actaully has TelegramUser(It’s a child of MessengerUser. I used multi-table inheritance).

Obviously the "hasattr(clien, “messenger_user”) won’t work, because it’s a DB query, not and object, so it will always return True.

My 2 solutions were

  1. Try-except where I try to reach the telegram_user “client.messenger_user.telegramuser” and return False if I get DoesNotExist error.
  2. Direct query on TelegramUser with id of client to check if it actually exists(), something like this
TelegramUser.objects.filter(client_id=client.id).exists()

Based on my time measurements using time.perf_counter() the second solution is 1.5-4 times faster. Would like to know what would you choose.

Your second case:

Is only valid if the OneToOneField relating Client to MessengerUser is the primary key. (By default, the OneToOneField created to relate TelegramUser with MessengerUser will have that set, so there’s nothing you need to do there.)

Otherwise you can use the hasattr. See the docs and examples at One-to-one relationships.

You might also want to review the docs at OneToOneField and Multi-table inheritance to possibly pick up on other things you may not currently know.

1 Like