Opened 3 years ago

Closed 3 years ago

#28268 closed New feature (wontfix)

Feature: Clear cached_property on related DB operations.

Reported by: Özer Sahin Owned by: nobody
Component: Database layer (models, ORM) Version: 1.11
Severity: Normal Keywords: cached_property
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Hey,

I am wondering, why it wouldn't be smarter to let the decorator (somehow) clear related caches whenever a related model changes.

Use Case:

I have a big model with many related models and I need to filter them for a ListView Page. To display those values I would use cached_property for data from other models like images. Since the related instances have not changed, we could use the cached_property. Now if we upload a new image, a signal could clear the cached_property used in the other model. The same goes for update, delete or other relevant operations. That way the system would always be up to date, but max out the potential of caching where ever it makes sense.

Is that clear? I am not an expert like you, but since I am currently learning about all this, I was curious why this hasn't been implemented (yet).

Change History (7)

comment:1 Changed 3 years ago by Tim Graham

Which cached properties are you referring to? Can you give an example shell session or something so the idea is a bit more concrete?

comment:2 Changed 3 years ago by Özer Sahin

Here is a quick example to try to illustrate:

class ModelA(models.Model):

    @cached_property
    def related_count(self):
        return self.related.count()

    @cached_property
    def related_content(self):
        return ', '.join([rel.title for rel in self.related.all()])


class Related(models.Model):

    model = models.ForeignKey(ModelA, related_name='related')
    title = models.CharField()

    def has_changed(self):
        # some method to determine if object changed
        return True

    def save(self):
        if not self.id or self.has_chaged():  # newly created or changed
            del self.model.related_count

    def delete(self):
        del self.model.related_count
   


>>> model = ModelA()
>>> model.related_count
0  # calls first time
>>> model.related_count
0  # does not call

>>> rel = Related()
>>> rel.model = model
>>> rel.title = "Text"
>>> rel.save()

>>> model.related_count
1  # calls first time, because rel added
>>> model.related_count
1  # does not call

>>> model.related_content
Text  # calls first time
>>> model.related_content
Text  # does not call

>>> rel.title = "New Text"
>>> rel.save()
>>> model.related_content
New Text  # calls first time, because rel changed
>>> model.related_content
New Text  # does not call
>>> model.related_count
1  # calls again, because rel changed

>>> rel.delete()
>>> model.related_count
0  # calls, because rel removed
>>> model.related_content
  # calls, return nothing

So what I am actually looking for, are some kind of signals to attach to the decorator so we can input logic, when the cached_property should be removed so it calls it again for an updated value. We could define that it should delete the cached value once related objects were updated, deleted or created. That way we can max out the cache, but keep it always up to date. I think it could be a huge performance booster without the issue of manually deleting cached_properties so they update.

I hope I am not too far off, awaiting your feedback!

Last edited 3 years ago by Özer Sahin (previous) (diff)

comment:3 Changed 3 years ago by Tim Graham

If I understand correctly, you want rel.save() to modify (clear the cached properties on) another, unrelated variable model. I'm not aware of any facility in Python to do that.

comment:4 Changed 3 years ago by Özer Sahin

I updated the example with save and delete methods and what should happen. Maybe that's more clear. The model is related (ForeignKey in class Related).

comment:5 Changed 3 years ago by Tim Graham

Is your idea that Django should automate the behavior in save() and delete() so you don't have to write that logic yourself? I still don't understand what change you want to make in Django. Is it impossible to implement your proposal as a third-party app?

comment:6 Changed 3 years ago by Özer Sahin

Yes, so Django should automatically take care of those operations in the background. Of course I could write a mixin or something similar, but that would bring overhead to the project.

Pydanny has a module, that is a configurable property: https://github.com/pydanny/cached-property

But that does not cover that case, I already asked and he referred me to the Django project.

I am not sure, if I could write an app that does all that. I thought it would be convenient if the cached_property could be called with kwargs like @cached_property(delete_on_save=True, delete_when_models_change=[ModelA, ModelB]).

Thank you for your time btw, really appreciate it!

comment:7 Changed 3 years ago by Tim Graham

Resolution: wontfix
Status: newclosed

Adding model related parameters to cached_property would require adding some sort of specialized cached_property since currently it's a general utility function usable on any Python class. All in all, it seems the ideas in this ticket aren't thought through thoroughly enough to say whether or not adding something to Django is required so I'm going to close the ticket for now. A third-party app for this use case might already exist. If you write some code to solve your problem and think it should be added to Django, or if you find that some change in Django is required to accomplish your use case, feel free to write to the DevelopersMailingList to get feedback about where to go from here.

Note: See TracTickets for help on using tickets.
Back to Top