我有一个 django-rest-framework 应用程序,目前在我的模型和序列化程序中大量使用计算属性。对于(过于简化的)示例:
模型.py
class Person(models.Model):
first_name = models.CharField()
last_name = models.CharField()
@property
full_name(self):
return first_name + last_name
序列化器.py
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ("first_name", "last_name", "full_name")
我有兴趣使用 Django 的 @cached_property而不是 @property,希望加快处理速度,因为这里计算的值实际上每天更改的次数不应超过一次。
不过,我不确定,如果@cached_property实际上会影响 DRF 返回它的 JSON 响应的速度。 Django 文档说:
The @cached_property decorator caches the result of a method with a single self argument as a property. The cached result will persist as long as the instance does, so if the instance is passed around and the function subsequently invoked, the cached result will be returned.
所以我想知道 Django 模型实例的生命周期是什么?每次调用 DRF View 时都会创建它吗?如果是这样,是否有其他方法可以实现我的目标?
请您参考如下方法:
@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)
