Оценка кверисетов Django и bulk_update с зависимостями
Предположим, у меня есть следующая модель (сильно упрощенная, ради вопроса):
class DailyMetric(models.Model):
value_1 = models.FloatField()
value_2 = models.FloatField()
ratio = models.FloatField()
Как следует из названия, для каждого дня будет своя запись. Однако value_1 и value_2 зависят от значений предыдущего дня. Допустим, расчет будет следующим:
def update_values(instance, previous_instance):
if previous_instance:
instance.value_1 = previous_instance.value_1 + 5
instance.value_2 = previous_instance.value_2 - 15
instance.ratio = instance.value_1 / instance.value_2
Естественно, если я обновляю значение для первого числа предыдущего месяца, мне приходится обновлять все последующие экземпляры. По причинам производительности я хочу использовать 'bulk_update':
# member method of the model class
def update_following(self):
following_entries = self.get_following_entries()
if not following_entries.exists():
return
for i in range(following_entries.count()):
if i == 0:
update_values(following_entries[i], self)
else:
update_values(following_entries[i], following_entries[i-1])
DailyMetric.objects.bulk_update(following_entries, ['value_1', 'value_2', 'ratio'])
Однако, похоже, что update_values всегда получает экземпляр со значениями, хранящимися в базе данных, даже если его "питоновское" представление было изменено в предыдущей итерации.
Если я предварительно привожу queryset к списку, то все работает, как и ожидалось. Но в моем понимании кверисет оценивается лениво, и при обращении к одному значению запрос должен быть выполнен, а данные затем "помещаются" в локальную память. Если это так, то почему значения "сбрасываются" в значения базы данных на следующей итерации?
Что я здесь упускаю? Есть ли в целом "лучший" способ решить эту задачу? Обновление (возможно) большого количества строк (~1-500), где значения зависят от "предыдущего" экземпляра?
Сабскриптинг делает запрос к базе данных, поэтому, пока значения не сохранены в базе данных, вы получаете старые значения.
Решением является простое приведение его к списку, так что при подзаписи будет получен i-ый элемент списка:
def update_following(self):
following_entries = list(self.get_following_entries())
for prev, cur in zip([self, *following_entries], following_entries):
update_values(cur, prev)
DailyMetric.objects.bulk_update(following_entries, ['value_1', 'value_2', 'ratio'])