【问题标题】:Django REST Framework & Lifecycle of a Django Model instanceDjango REST 框架和 Django 模型实例的生命周期
【发布时间】:2019-09-07 02:15:41
【问题描述】:

我有一个 django-rest-framework 应用程序,它目前在我的模型和序列化程序中大量使用计算属性。对于(过度简化的)示例:

models.py

class Person(models.Model):
    first_name = models.CharField()
    last_name = models.CharField()

    @property
    full_name(self):
        return first_name + last_name

serializers.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 文档说:

@cached_property 装饰器缓存具有单个 self 参数的方法的结果作为属性。缓存的结果会随着实例的存在而持续存在,因此如果实例被传递并且随后调用了函数,则将返回缓存的结果。

所以我想知道 Django 模型实例的生命周期是什么?每次调用 DRF 视图时都会创建它吗?如果是这样,是否有其他方法可以实现我的目标?

【问题讨论】:

    标签: python django caching django-rest-framework


    【解决方案1】:

    @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)
    

    【讨论】:

    • 感谢您的周到回复。很有意思。我会在接受之前稍等片刻,看看是否还有其他想法。
    猜你喜欢
    • 1970-01-01
    • 2017-03-27
    • 2018-12-10
    • 2018-06-13
    • 1970-01-01
    • 2015-08-07
    • 2015-01-04
    • 1970-01-01
    • 2016-05-30
    相关资源
    最近更新 更多