@cached_property 装饰器似乎与内存中的实例一起使用,类似于查询集的缓存方式。这是我的意思的一个小例子:
# Model that stores two numbers and returns their sum (slowly)
class MyModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
@cached_property
def sum(self):
time.sleep(10)
return a + b
my_model = MyModel(a=1, b=2)
my_model.save()
print(my_model.sum) # This will take 10 seconds because of the sleep
print(my_model.sum) # This will return instantly because the result is cached and it doesn't need to execute the properties code
my_model_again = MyModel.objects.first() # Lets fetch that model again from the database.
print(my_model_again.sum) # This will take 10 seconds because of the sleep
# We can pass the instance around into other functions, and because it is still the same object it will leverage the cached property
def double_sum(instance):
return instance.sum + instance.sum
print(double_sum(my_model_again)) # This will print almost instantly
在此示例中,我使用 sleep 来模拟冗长或计算量大的计算。
虽然my_model 和my_model_again 代表相同的数据库行,但它们是内存中的不同实例。您必须传递实例以利用缓存的属性。
如果您希望缓存在该对象的所有实例之间持续存在,您可以将结果存储在数据库中并每 24 小时使它们失效。下面是一个使用数据库的简单示例:
class MyModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
cached_sum = models.IntegerField(default=None, null=True, blank=True)
cached_sum_timestamp = models.DateTimeField()
@property
def sum(self):
# If the sum has been calculated in the last 24 hours, return the cached sum, otherwise recalculate
if (
self.cached_sum
and self.cached_sum_timestamp
and self.cached_sum_timestamp > (timezone.now() - timezone.timedelta(days=1)
):
return self.cached_sum
time.sleep(10)
self.cached_sum = self.a + self.b
self.cached_sum_timestamp = timezone.now()
self.save(update_fields=("cached_sum", "cached_sum_timestamp"))
return self.cached_sum
但是,您可能还希望在模型更改时使缓存无效......这是可能的,但它变得有点困难。缓存失效可能很难正确处理。这是一个在实例字段更改时使缓存失效的相当幼稚的示例:
class MyModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
cached_sum = models.IntegerField(default=None, null=True, blank=True)
@property
def sum(self):
if self.cached_sum:
return self.cached_sum
time.sleep(10)
self.cached_sum = self.a + self.b
self.save(update_fields=("cached_sum", ))
return self.cached_sum
def save(self, *args, **kwargs):
if self.pk: # Compare the object we're saving with whats in the database, and if the cache should be invalidated then set cached_sum to None
db_obj = MyModel.objects.get(pk=self.pk)
if db_obj.a != self.a or db_obj.b != self.b:
self.cached_sum = None
else:
# It would be nice to cache this when we create the object?
if self.cached_sum is not None:
self.cached_sum = self.sum
return super().save(*args, **kwargs)