How Can I Verify that Django @cached_property is Working (caching)?

848 views Asked by At

In the below example, I have questions.

Example

from django.utils.functional import cached_property

class Product(models.Model):
    class ProductType(models.TextChoices):
        PRODUCT = 'PRODUCT', _('Product')
        LISTING = 'LISTING', _('Listing')

    my_model = models.ForeignKey(MyModel, on_delete=models.CASCADE, related_name='products')
    product_type = models.CharField(max_length=7, choices=ProductType.choices)

class MyModel(models.Model):
    ...
    
    @cached_property
    def listing(self):
        return self.products.get(product_type=Product.ProductType.LISTING)

Questions

  1. Is the listing property being cached on the MyModel object? I ask because it's accessing .get() of a queryset which has greater implications.

Ex:

instance = MyModel()
instance.listing # hits db
instance.listing # gets from cache?
  1. How can I set up a scenario to inspect and verify that caching of instance.listing is in-fact happening? I read to look in the __dict__ method of instance.listing.__dict__ but I don't notice anything specific.
3

There are 3 answers

1
Sardorbek Imomaliev On BEST ANSWER

If you read source of this decorator https://docs.djangoproject.com/en/4.0/_modules/django/utils/functional/#cached_property you will see that you are probably checking this in wrong place. listing will be cached on the instance itself, meaning you need to check instance.__dict__.

0
Jarad On

I awarded @Sardorbek Imomaliev with the answer for pointing me in the right direction. For clarity to anyone that stumbles across this question, here are my findings.

  1. Is the listing property being cached on the MyModel object? I ask because it's accessing .get() of a queryset which has greater implications.

Answer: yes. But, if the cached object's attributes change, these changes aren't visible in the cached object. Ex: if price changes from $4 to $5, the cached property will still show $4.

  1. How can I set up a scenario to inspect and verify that caching of instance.listing is in-fact happening?

Answer:

>>> inst = MyModel.objects.first()
>>> inst.__dict__.keys()
dict_keys(['_state', 'id', 'user_id', 'title', 'slug'])
>>> inst.listing
<Listing: Some Listing Instance>
>>> inst.__dict__.keys()
dict_keys(['_state', 'id', 'user_id', 'title', 'slug', 'listing'])
  1. Bonus: What does it look like if it's not a @cached_property but is just a regular @property?

Answer: 'listing' does not get added to inst.__dict__.

>>> inst = MyModel.objects.first()
>>> inst.__dict__.keys()
dict_keys(['_state', 'id', 'user_id', 'title', 'slug'])
>>> inst.listing
<Listing: Some Listing Instance>
>>> inst.__dict__.keys()
dict_keys(['_state', 'id', 'user_id', 'title', 'slug'])
0
Dante On

to delete the cached property,you can use del inst.listing.so you can basically use signals to delete this property cache whenever the Product model is changed