Как Django узнает о существовании объекта

Допустим, у меня есть две модели, например:

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()

Пожалуйста, не беспокойтесь о том, почему кому-то могут понадобиться две разные модели/таблицы для таких данных. Это не главное здесь. Что интересно, когда я запрашиваю Product, я получу python attr productprice вместе с объектом ТОЛЬКО ЕСЛИ ProductPrice существуют связанные с этим Product.

Чтобы сделать это более понятным, давайте создадим некоторые фиктивные данные:

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)

Теперь попробуйте это

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

Как django узнает о существовании ProductPrice в базе данных? В SQL я не знаю никакого способа проверить это без второго запроса. Мне очень интересно, как это работает.

Как django узнает, что ProductPrice существует в базе данных?

Выполняет SQL-запрос. Действительно, он отслеживает все отношения и определяет для каждого отношения одно в противоположном направлении. Это свойства, которые являются лениво загруженными.

Это означает, что если вы используете hasattr(product1, 'productprice'), то запрос будет выглядеть следующим образом:

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

В случае если запрос вернет запись, он вернет True для этого атрибута, в случае если база данных не вернет ни одной записи, это вызовет ошибку AttributeError, и таким образом hasattr(…) вернет False.

Таким образом, эти запросы выполняются лабильно: только когда вы обращаетесь к атрибуту .productprice, он будет делать такой запрос, пока вы не обращаетесь к этому атрибуту, запрос не выполняется.

Если мы, например, посмотрим на последний запрос, который Django выполняет с помощью print(connection.queries[-1]), мы увидим:

>>> 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'}
Вернуться на верх