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
- Try-except where I try to reach the telegram_user “client.messenger_user.telegramuser” and return False if I get DoesNotExist error.
- 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