How does Django know if object exists

Viewed 67

Let's say I have two models, for example:

class Product:
  product_name = models.CharField(max_length=40)
class ProductPrice(models.Model):
  product = models.OneToOneField(Product, on_delete=models.CASCADE)
  product_price = models.FloatField()

Please don't bother why someone might want two different models/tables for such data. That's not the main point here. What's interesting is, when I query a Product I will get a python attr productprice along with the object ONLY IF ProductPrice exist associated with that Product.

To make this more clear let's create some dummy data:

product1 = Product.objects.create(product_name='first product')
product2 = Product.objects.create(product_name='second product')
product_price = ProductPrice.objects.create(product=product2, price=10.99)

Now try this

print(hasattr(product1`, "productprice")) # -> false
print(hasattr(product2`, "productprice")) # -> true

How does django know that a ProductPrice exist in database? In SQL I am not aware of any way to check this without second query. I am very curious how this works.

1 Answers

How does django know that a ProductPrice exist in database?

It makes an SQL query. Indeed, it keeps track of all relations and defines for each relation one in the opposite direction. These are properties that are lazy loaded.

This thus means that if you use hasattr(product1, 'productprice'), it will make a query that looks like:

SELECT appname_productprice.*
FROM appname_productprice
WHERE appname_productprice.product_id = some_id

In case the query returns a record, it will return True for that attribute, in case the database returns no record, this will raise an AttributeError, and thus hasattr(…) will return False.

These queries are thus done lazily: only when you access the attribute .productprice it will make such query, as long as you do not access that attribute, no query is performed.

If we for example look at the last query that Django runs with print(connection.queries[-1]), we see:

>>> from django.db import connection
>>> print(hasattr(product1, 'productprice'))
False
>>> print(connection.queries[-1])
{'sql': 'SELECT `appname_productprice`.`id`, `appname_productprice`.`product_id`, `appname_productprice`.`product_price` FROM `appname_productprice` WHERE `appname_productprice`.`product_id` = 1 LIMIT 21', 'time': '0.001'}
>>> print(hasattr(product2, 'productprice'))
True
>>> print(connection.queries[-1])
{'sql': 'SELECT `appname_productprice`.`id`, `appname_productprice`.`product_id`, `appname_productprice`.`product_price` FROM `appname_productprice` WHERE `appname_productprice`.`product_id` = 2 LIMIT 21', 'time': '0.001'}
Related