Skip to main content
 首页 » 编程设计

python之Django REST Framework 和 Django 模型实例的生命周期

2024年11月24日25insus

我有一个 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)