mirror of
https://github.com/Hopiu/django-model-utils.git
synced 2026-03-17 04:10:24 +00:00
Use a signal handler instead of patching save.
References #83. Instead of patching the save method of a tracked model class, we can use a signal handler on post_save, which means we can still pickle our model class. Note we can't just listen for the signal from the class we have, but instead listen for all post_save signals. This means we actually install a new signal handler for each tracked model class, which fires on all model save occurrences (and returns immediately if this handler doesn't care). We probably could improve this to have a registry of tracked models, or something, that allows us to just install one signal handler, and filter according to membership.
This commit is contained in:
parent
fdf20e9d13
commit
3496fe4291
2 changed files with 24 additions and 17 deletions
|
|
@ -1497,6 +1497,9 @@ class FieldTrackerTests(FieldTrackerTestCase, FieldTrackerCommonTests):
|
|||
|
||||
item.number = 2
|
||||
self.assertTrue(item.tracker.has_changed('number'))
|
||||
|
||||
def test_can_pickle_objects(self):
|
||||
pickle.dumps(self.instance)
|
||||
|
||||
|
||||
class FieldTrackedModelCustomTests(FieldTrackerTestCase,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ from __future__ import unicode_literals
|
|||
|
||||
from copy import deepcopy
|
||||
|
||||
from django.db import models
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import models
|
||||
from django.db.models.query_utils import DeferredAttribute
|
||||
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
class FieldInstanceTracker(object):
|
||||
def __init__(self, instance, fields, field_map):
|
||||
|
|
@ -119,20 +120,16 @@ class FieldTracker(object):
|
|||
models.signals.post_init.connect(self.initialize_tracker)
|
||||
self.model_class = sender
|
||||
setattr(sender, self.name, self)
|
||||
|
||||
def initialize_tracker(self, sender, instance, **kwargs):
|
||||
if not isinstance(instance, self.model_class):
|
||||
return # Only init instances of given model (including children)
|
||||
tracker = self.tracker_class(instance, self.fields, self.field_map)
|
||||
setattr(instance, self.attname, tracker)
|
||||
tracker.set_saved_fields()
|
||||
self.patch_save(instance)
|
||||
|
||||
def patch_save(self, instance):
|
||||
original_save = instance.save
|
||||
def save(**kwargs):
|
||||
ret = original_save(**kwargs)
|
||||
|
||||
# Rather than patch the save method on the instance,
|
||||
# we can observe the post_save signal on the class.
|
||||
@receiver(post_save, sender=None, weak=False)
|
||||
def handler(sender, instance, **kwargs):
|
||||
if not isinstance(instance, self.model_class):
|
||||
return
|
||||
|
||||
update_fields = kwargs.get('update_fields')
|
||||
|
||||
if not update_fields and update_fields is not None: # () or []
|
||||
fields = update_fields
|
||||
elif update_fields is None:
|
||||
|
|
@ -142,12 +139,19 @@ class FieldTracker(object):
|
|||
field for field in update_fields if
|
||||
field in self.fields
|
||||
)
|
||||
|
||||
getattr(instance, self.attname).set_saved_fields(
|
||||
fields=fields
|
||||
)
|
||||
return ret
|
||||
instance.save = save
|
||||
|
||||
|
||||
def initialize_tracker(self, sender, instance, **kwargs):
|
||||
if not isinstance(instance, self.model_class):
|
||||
return # Only init instances of given model (including children)
|
||||
tracker = self.tracker_class(instance, self.fields, self.field_map)
|
||||
setattr(instance, self.attname, tracker)
|
||||
tracker.set_saved_fields()
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
return self
|
||||
|
|
|
|||
Loading…
Reference in a new issue