ChangedObject m2m implementation
This commit is contained in:
parent
15e0b3cad2
commit
eb326a3694
4 changed files with 68 additions and 22 deletions
|
@ -33,8 +33,8 @@ class ChangedObject(models.Model):
|
|||
model_class = kwargs.pop('model_class', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._set_object = None
|
||||
self._m2m_added_cache = {name: set(values) for name, values in self.m2m_added}
|
||||
self._m2m_removed_cache = {name: set(values) for name, values in self.m2m_added}
|
||||
self._m2m_added_cache = {name: set(values) for name, values in self.m2m_added.items()}
|
||||
self._m2m_removed_cache = {name: set(values) for name, values in self.m2m_removed.items()}
|
||||
if model_class is not None:
|
||||
self.model_class = model_class
|
||||
|
||||
|
@ -114,8 +114,8 @@ class ChangedObject(models.Model):
|
|||
self.changeset.deleted_existing.setdefault(model, set()).add(pk)
|
||||
|
||||
if not self.deleted:
|
||||
self.changeset.m2m_added.get(model, {})[pk] = self._m2m_added_cache
|
||||
self.changeset.m2m_removed.get(model, {})[pk] = self._m2m_removed_cache
|
||||
self.changeset.m2m_added.setdefault(model, {})[pk] = self._m2m_added_cache
|
||||
self.changeset.m2m_removed.setdefault(model, {})[pk] = self._m2m_removed_cache
|
||||
else:
|
||||
self.changeset.m2m_added.get(model, {}).pop(pk, None)
|
||||
self.changeset.m2m_removed.get(model, {}).pop(pk, None)
|
||||
|
@ -189,8 +189,6 @@ class ChangedObject(models.Model):
|
|||
else:
|
||||
value = None if value is None else value.pk
|
||||
self.updated_fields[field.name] = value
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
self.clean_updated_fields()
|
||||
self.save()
|
||||
|
@ -201,6 +199,46 @@ class ChangedObject(models.Model):
|
|||
self.deleted = True
|
||||
self.save()
|
||||
|
||||
def m2m_set(self, name, set_pks=None):
|
||||
if self.is_created:
|
||||
self._m2m_removed_cache.get(name, None)
|
||||
return
|
||||
|
||||
field = self.model_class._meta.get_field(name)
|
||||
rel_name = field.rel.related_name
|
||||
pks = set(field.related_model.objects.filter(**{rel_name+'__pk': self.obj_pk}).values_list('pk', flat=True))
|
||||
|
||||
m2m_added_before = self._m2m_added_cache.get(name, set())
|
||||
m2m_removed_before = self._m2m_removed_cache.get(name, set())
|
||||
|
||||
if set_pks is None:
|
||||
self._m2m_added_cache.get(name, set()).difference_update(pks)
|
||||
self._m2m_removed_cache.get(name, set()).intersection_update(pks)
|
||||
else:
|
||||
self._m2m_added_cache[name] = set_pks - pks
|
||||
self._m2m_removed_cache[name] = pks - set_pks
|
||||
|
||||
if not self._m2m_added_cache.get(name, set()):
|
||||
self._m2m_added_cache.pop(name, None)
|
||||
if not self._m2m_removed_cache.get(name, set()):
|
||||
self._m2m_removed_cache.pop(name, None)
|
||||
|
||||
if (m2m_added_before != self._m2m_added_cache.get(name, set()) or
|
||||
m2m_removed_before != self._m2m_removed_cache.get(name, set())):
|
||||
self.save()
|
||||
return True
|
||||
return False
|
||||
|
||||
def m2m_add(self, name, pks: set):
|
||||
self._m2m_added_cache.setdefault(name, set()).update(pks)
|
||||
self._m2m_removed_cache.setdefault(name, set()).difference_update(pks)
|
||||
self.m2m_set(name)
|
||||
|
||||
def m2m_remove(self, name, pks: set):
|
||||
self._m2m_removed_cache.setdefault(name, set()).update(pks)
|
||||
self._m2m_added_cache.setdefault(name, set()).difference_update(pks)
|
||||
self.m2m_set(name)
|
||||
|
||||
@property
|
||||
def does_something(self):
|
||||
return (self.updated_fields or self._m2m_added_cache or self._m2m_removed_cache or self.is_created or
|
||||
|
@ -213,8 +251,11 @@ class ChangedObject(models.Model):
|
|||
if self.pk is not None:
|
||||
self.delete()
|
||||
return False
|
||||
self.m2m_added = {name: tuple(values) for name, values in self._m2m_added_cache}
|
||||
self.m2m_removed = {name: tuple(values) for name, values in self._m2m_removed_cache}
|
||||
if self.changeset.pk is None:
|
||||
self.changeset.save()
|
||||
self.changeset = self.changeset
|
||||
self.m2m_added = {name: tuple(values) for name, values in self._m2m_added_cache.items()}
|
||||
self.m2m_removed = {name: tuple(values) for name, values in self._m2m_removed_cache.items()}
|
||||
super().save(*args, **kwargs)
|
||||
if not self.changeset.fill_changes_cache():
|
||||
self.update_changeset_cache()
|
||||
|
|
|
@ -131,7 +131,11 @@ class ChangeSet(models.Model):
|
|||
:param include_deleted_created: Fetch created objects that were deleted.
|
||||
:rtype: True if the method was executed, else False
|
||||
"""
|
||||
if self.pk is None or self.changed_objects is not None:
|
||||
if self.changed_objects is not None:
|
||||
return False
|
||||
|
||||
if self.pk is None:
|
||||
self.changed_objects = {}
|
||||
return False
|
||||
|
||||
if include_deleted_created:
|
||||
|
|
|
@ -142,10 +142,10 @@ def changeset_detail(request, pk):
|
|||
|
||||
for m2m_mode in ('m2m_added', 'm2m_removed'):
|
||||
m2m_list = getattr(changed_object, m2m_mode).items()
|
||||
for name, values in sorted(m2m_list, key=lambda name, value: form_fields.index(name)):
|
||||
for name, values in sorted(m2m_list, key=lambda nv: form_fields.index(nv[0])):
|
||||
field = model._meta.get_field(name)
|
||||
for value in values:
|
||||
change_data.update({
|
||||
changes.append({
|
||||
'icon': 'chevron-right' if m2m_mode == 'm2m_added' else 'chevron-left',
|
||||
'class': 'info',
|
||||
'title': field.verbose_name,
|
||||
|
|
|
@ -739,21 +739,22 @@ class ManyRelatedManagerWrapper(RelatedManagerWrapper):
|
|||
return self._obj.prefetch_cache_name
|
||||
|
||||
def set(self, objs):
|
||||
old_ids = set(self.values_list('pk', flat=True))
|
||||
new_ids = set(obj.pk for obj in objs)
|
||||
|
||||
self.remove(*(old_ids - new_ids))
|
||||
self.add(*(new_ids - old_ids))
|
||||
if self._obj.reverse:
|
||||
raise NotImplementedError
|
||||
pks = set(obj.pk for obj in objs)
|
||||
self._changeset.get_changed_object(self._obj.instance).m2m_set(self._get_cache_name(), pks)
|
||||
|
||||
def add(self, *objs):
|
||||
for obj in objs:
|
||||
pk = (obj.pk if isinstance(obj, self._obj.model) else obj)
|
||||
self._changeset.add_m2m_add(self._obj.instance, name=self._get_cache_name(), value=pk)
|
||||
if self._obj.reverse:
|
||||
raise NotImplementedError
|
||||
pks = set((obj.pk if isinstance(obj, self._obj.model) else obj) for obj in objs)
|
||||
self._changeset.get_changed_object(self._obj.instance).m2m_add(self._get_cache_name(), pks)
|
||||
|
||||
def remove(self, *objs):
|
||||
for obj in objs:
|
||||
pk = (obj.pk if isinstance(obj, self._obj.model) else obj)
|
||||
self._changeset.add_m2m_remove(self._obj.instance, name=self._get_cache_name(), value=pk)
|
||||
if self._obj.reverse:
|
||||
raise NotImplementedError
|
||||
pks = set((obj.pk if isinstance(obj, self._obj.model) else obj) for obj in objs)
|
||||
self._changeset.get_changed_object(self._obj.instance).m2m_remove(self._get_cache_name(), pks)
|
||||
|
||||
def all(self):
|
||||
try:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue