[Django] #22543: Add pre_update and post_update signal for querysets

25 views
Skip to first unread message

Django

unread,
Apr 29, 2014, 5:41:53 PM4/29/14
to django-...@googlegroups.com
#22543: Add pre_update and post_update signal for querysets
----------------------------------------------+----------------------------
Reporter: bendavis78 | Owner: nobody
Type: New feature | Status: new
Component: Database layer (models, ORM) | Version: master
Severity: Normal | Keywords: queryset
Triage Stage: Unreviewed | update signals
Easy pickings: 0 | Has patch: 0
| UI/UX: 0
----------------------------------------------+----------------------------
One use case for this would be cache coherence. For example, let's say
we're caching an expensive operation based on a model pk, and we want to
invalidate that cache when a row is changed in any way. Currently we can
only do this with save/delete signals. Consider the following example for
calculating points awarded to an account:

{{{
#!python
def get_points_earned(account):
key = 'acct-{0}-points'.format(account.id)
if not cache.get(key):
point_awards = PointAward.objects.filter(account=account)
points = point_awards.aggregate(total=Sum('points'))['total']
cache.set(key, points)
return cache.get(key)

# Invalidate points earned if a PointAward is added/changed/deleted
@receiver([post_save, post_delete, post_update], sender=PointAward)
def on_point_award_changed(sender, **kwargs):
instance = kwargs.get('instance')
if instance:
key = 'acct-{0}-points'.format(instance.id)
cache.delete(key)
}}}

With this coherence strategy, a call to `PointAward.objects.update()` will
cause the cache to become incoherent, and will cause errors in the
calculation.

This can easily be addressed by providing a signal allowing a developer
to decide how best to update the cache when `update()` is called:

{{{
#!python
# django/db/models/signals.py

pre_update = ModelSignal(providing_args=["queryset", "values", "using"],
use_caching=True)
post_update = ModelSignal(providing_args=["queryset", "values", "using"],
use_caching=True)
}}}

We can now update our receiver to also respond to updates as well as
changes and deletions:

{{{
#!python

@receiver([post_save, post_delete], sender=PointAward)
def on_point_award_changed(sender, **kwargs):
point_award = kwargs.get('instance')
if instance and instance.pk:
# this is a save/delete, invalidate the related account's points
account_id = point_award.account_id
key = 'acct-{0}-points'.format(instance.id)
cache.delete(key)

elif kwargs.get('queryset'):
# this is an update, invalidate all matching accounts
qs = kwargs['queryset']
for acct_id in qs.objects.distinct().values_list('account_id',
flat=True)
key = 'acct-{0}-points'.format(acct_id)
cache.delete(key)
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/22543>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Apr 29, 2014, 6:13:24 PM4/29/14
to django-...@googlegroups.com
#22543: Add pre_update and post_update signal for querysets
-------------------------------------+-------------------------------------
Reporter: bendavis78 | Owner: nobody
Type: New feature | Status: closed
Component: Database layer | Version: master
(models, ORM) | Resolution: duplicate
Severity: Normal | Triage Stage:
Keywords: queryset update | Unreviewed
signals | Needs documentation: 0
Has patch: 0 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by loic84):

* status: new => closed
* needs_better_patch: => 0
* resolution: => duplicate
* needs_tests: => 0
* needs_docs: => 0


Comment:

Hi @bendavis78, this feature is discussed at #21461.

--
Ticket URL: <https://code.djangoproject.com/ticket/22543#comment:1>

Django

unread,
Apr 29, 2014, 6:15:50 PM4/29/14
to django-...@googlegroups.com
#22543: Add pre_update and post_update signal for querysets
-------------------------------------+-------------------------------------
Reporter: bendavis78 | Owner: nobody

Type: New feature | Status: closed
Component: Database layer | Version: master
(models, ORM) | Resolution: wontfix

Severity: Normal | Triage Stage:
Keywords: queryset update | Unreviewed
signals | Needs documentation: 0
Has patch: 0 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by timo):

* resolution: duplicate => wontfix


Comment:

Seems this is similar to #17824 which was won't fixed. Needs discussion on
the django-developers mailing list in order to be accepted.

--
Ticket URL: <https://code.djangoproject.com/ticket/22543#comment:2>

Reply all
Reply to author
Forward
0 new messages