Home > database >  Django queryset evaluation and bulk_update with dependencies
Django queryset evaluation and bulk_update with dependencies

Time:01-30

Suppose I have the following model (vastly simplified, for the sake of the question):

class DailyMetric(models.Model):
 value_1 = models.FloatField()
 value_2 = models.FloatField()
 ratio = models.FloatField()

As the name suggests, there will be an entry for each day. However, value_1 and value_2 depend on the values from the previous day. Let's say, the calculation is as follows:

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

Naturally, if I update the value for the first of the previous month, I have to update all following instance. For performance reasons, I want to make use of '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'])

However, it seems, that update_values always gets the instance with the values stored in the database, even though its "python" representation has been modified in the previous iteration. If I cast the queryset to a list beforehand, it works as expected. But in my understanding a queryset is evaluated lazy, and by accessing one value, the query has to be executed and the data then "resides" in local memory. If so, why are the values "reset" to the database values in the next iteration?

What am I missing here? Is there an overall "better" way to accomplish this? Updating a (possibly) large amount of rows (~1-500) where the values depend on a "previous" instance?

CodePudding user response:

Subscripting makes a query to the database, so as long as the values are not saved to the database, you get the old ones.

A solution is to simply cast it to a list, such that subscripting will obtain the i-th element of the list:

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'])
  •  Tags:  
  • Related