mirror of
https://github.com/django/django.git
synced 2025-11-19 11:15:44 +00:00
Merge 47f8206609 into 1ce6e78dd4
This commit is contained in:
commit
737bddeb6f
7 changed files with 202 additions and 22 deletions
|
|
@ -262,11 +262,11 @@ class DeserializedObject:
|
||||||
def save(self, save_m2m=True, using=None, **kwargs):
|
def save(self, save_m2m=True, using=None, **kwargs):
|
||||||
# Call save on the Model baseclass directly. This bypasses any
|
# Call save on the Model baseclass directly. This bypasses any
|
||||||
# model-defined save. The save is also forced to be raw.
|
# model-defined save. The save is also forced to be raw.
|
||||||
# raw=True is passed to any pre/post_save signals.
|
# raw=True is passed to any pre/post_save and m2m_changed signals.
|
||||||
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
|
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
|
||||||
if self.m2m_data and save_m2m:
|
if self.m2m_data and save_m2m:
|
||||||
for accessor_name, object_list in self.m2m_data.items():
|
for accessor_name, object_list in self.m2m_data.items():
|
||||||
getattr(self.object, accessor_name).set(object_list)
|
getattr(self.object, accessor_name).set_base(object_list, raw=True)
|
||||||
|
|
||||||
# prevent a second (possibly accidental) call to save() from saving
|
# prevent a second (possibly accidental) call to save() from saving
|
||||||
# the m2m data twice.
|
# the m2m data twice.
|
||||||
|
|
|
||||||
|
|
@ -1262,15 +1262,16 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
else:
|
else:
|
||||||
return super().count()
|
return super().count()
|
||||||
|
|
||||||
def add(self, *objs, through_defaults=None):
|
def _add_base(self, *objs, through_defaults=None, using=None, raw=False):
|
||||||
self._remove_prefetched_objects()
|
db = using or router.db_for_write(self.through, instance=self.instance)
|
||||||
db = router.db_for_write(self.through, instance=self.instance)
|
|
||||||
with transaction.atomic(using=db, savepoint=False):
|
with transaction.atomic(using=db, savepoint=False):
|
||||||
self._add_items(
|
self._add_items(
|
||||||
self.source_field_name,
|
self.source_field_name,
|
||||||
self.target_field_name,
|
self.target_field_name,
|
||||||
*objs,
|
*objs,
|
||||||
through_defaults=through_defaults,
|
through_defaults=through_defaults,
|
||||||
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
# If this is a symmetrical m2m relation to self, add the mirror
|
# If this is a symmetrical m2m relation to self, add the mirror
|
||||||
# entry in the m2m table.
|
# entry in the m2m table.
|
||||||
|
|
@ -1280,8 +1281,15 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
self.source_field_name,
|
self.source_field_name,
|
||||||
*objs,
|
*objs,
|
||||||
through_defaults=through_defaults,
|
through_defaults=through_defaults,
|
||||||
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def add(self, *objs, through_defaults=None):
|
||||||
|
self._remove_prefetched_objects()
|
||||||
|
db = router.db_for_write(self.through, instance=self.instance)
|
||||||
|
self._add_base(*objs, through_defaults=through_defaults, using=db)
|
||||||
|
|
||||||
add.alters_data = True
|
add.alters_data = True
|
||||||
|
|
||||||
async def aadd(self, *objs, through_defaults=None):
|
async def aadd(self, *objs, through_defaults=None):
|
||||||
|
|
@ -1291,9 +1299,16 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
|
|
||||||
aadd.alters_data = True
|
aadd.alters_data = True
|
||||||
|
|
||||||
|
def _remove_base(self, *objs, using=None, raw=False):
|
||||||
|
db = using or router.db_for_write(self.through, instance=self.instance)
|
||||||
|
self._remove_items(
|
||||||
|
self.source_field_name, self.target_field_name, *objs, using=db, raw=raw
|
||||||
|
)
|
||||||
|
|
||||||
def remove(self, *objs):
|
def remove(self, *objs):
|
||||||
self._remove_prefetched_objects()
|
self._remove_prefetched_objects()
|
||||||
self._remove_items(self.source_field_name, self.target_field_name, *objs)
|
db = router.db_for_write(self.through, instance=self.instance)
|
||||||
|
self._remove_base(*objs, using=db)
|
||||||
|
|
||||||
remove.alters_data = True
|
remove.alters_data = True
|
||||||
|
|
||||||
|
|
@ -1302,8 +1317,8 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
|
|
||||||
aremove.alters_data = True
|
aremove.alters_data = True
|
||||||
|
|
||||||
def clear(self):
|
def _clear_base(self, using=None, raw=False):
|
||||||
db = router.db_for_write(self.through, instance=self.instance)
|
db = using or router.db_for_write(self.through, instance=self.instance)
|
||||||
with transaction.atomic(using=db, savepoint=False):
|
with transaction.atomic(using=db, savepoint=False):
|
||||||
signals.m2m_changed.send(
|
signals.m2m_changed.send(
|
||||||
sender=self.through,
|
sender=self.through,
|
||||||
|
|
@ -1313,8 +1328,8 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
model=self.model,
|
model=self.model,
|
||||||
pk_set=None,
|
pk_set=None,
|
||||||
using=db,
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
self._remove_prefetched_objects()
|
|
||||||
filters = self._build_remove_filters(super().get_queryset().using(db))
|
filters = self._build_remove_filters(super().get_queryset().using(db))
|
||||||
self.through._default_manager.using(db).filter(filters).delete()
|
self.through._default_manager.using(db).filter(filters).delete()
|
||||||
|
|
||||||
|
|
@ -1326,8 +1341,14 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
model=self.model,
|
model=self.model,
|
||||||
pk_set=None,
|
pk_set=None,
|
||||||
using=db,
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._remove_prefetched_objects()
|
||||||
|
db = router.db_for_write(self.through, instance=self.instance)
|
||||||
|
self._clear_base(using=db)
|
||||||
|
|
||||||
clear.alters_data = True
|
clear.alters_data = True
|
||||||
|
|
||||||
async def aclear(self):
|
async def aclear(self):
|
||||||
|
|
@ -1335,16 +1356,19 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
|
|
||||||
aclear.alters_data = True
|
aclear.alters_data = True
|
||||||
|
|
||||||
def set(self, objs, *, clear=False, through_defaults=None):
|
def set_base(self, objs, *, clear=False, through_defaults=None, raw=False):
|
||||||
# Force evaluation of `objs` in case it's a queryset whose value
|
# Force evaluation of `objs` in case it's a queryset whose value
|
||||||
# could be affected by `manager.clear()`. Refs #19816.
|
# could be affected by `manager.clear()`. Refs #19816.
|
||||||
objs = tuple(objs)
|
objs = tuple(objs)
|
||||||
|
|
||||||
db = router.db_for_write(self.through, instance=self.instance)
|
db = router.db_for_write(self.through, instance=self.instance)
|
||||||
with transaction.atomic(using=db, savepoint=False):
|
with transaction.atomic(using=db, savepoint=False):
|
||||||
|
self._remove_prefetched_objects()
|
||||||
if clear:
|
if clear:
|
||||||
self.clear()
|
self._clear_base(using=db, raw=raw)
|
||||||
self.add(*objs, through_defaults=through_defaults)
|
self._add_base(
|
||||||
|
*objs, through_defaults=through_defaults, using=db, raw=raw
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
old_ids = set(
|
old_ids = set(
|
||||||
self.using(db).values_list(
|
self.using(db).values_list(
|
||||||
|
|
@ -1364,8 +1388,13 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
else:
|
else:
|
||||||
new_objs.append(obj)
|
new_objs.append(obj)
|
||||||
|
|
||||||
self.remove(*old_ids)
|
self._remove_base(*old_ids, using=db, raw=raw)
|
||||||
self.add(*new_objs, through_defaults=through_defaults)
|
self._add_base(
|
||||||
|
*new_objs, through_defaults=through_defaults, using=db, raw=raw
|
||||||
|
)
|
||||||
|
|
||||||
|
def set(self, objs, *, clear=False, through_defaults=None):
|
||||||
|
self.set_base(objs, clear=clear, through_defaults=through_defaults)
|
||||||
|
|
||||||
set.alters_data = True
|
set.alters_data = True
|
||||||
|
|
||||||
|
|
@ -1516,7 +1545,13 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
)
|
)
|
||||||
|
|
||||||
def _add_items(
|
def _add_items(
|
||||||
self, source_field_name, target_field_name, *objs, through_defaults=None
|
self,
|
||||||
|
source_field_name,
|
||||||
|
target_field_name,
|
||||||
|
*objs,
|
||||||
|
through_defaults=None,
|
||||||
|
using=None,
|
||||||
|
raw=False,
|
||||||
):
|
):
|
||||||
# source_field_name: the PK fieldname in join table for the source
|
# source_field_name: the PK fieldname in join table for the source
|
||||||
# object target_field_name: the PK fieldname in join table for the
|
# object target_field_name: the PK fieldname in join table for the
|
||||||
|
|
@ -1527,7 +1562,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
|
|
||||||
through_defaults = dict(resolve_callables(through_defaults or {}))
|
through_defaults = dict(resolve_callables(through_defaults or {}))
|
||||||
target_ids = self._get_target_ids(target_field_name, objs)
|
target_ids = self._get_target_ids(target_field_name, objs)
|
||||||
db = router.db_for_write(self.through, instance=self.instance)
|
db = using or router.db_for_write(self.through, instance=self.instance)
|
||||||
can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan(
|
can_ignore_conflicts, must_send_signals, can_fast_add = self._get_add_plan(
|
||||||
db, source_field_name
|
db, source_field_name
|
||||||
)
|
)
|
||||||
|
|
@ -1559,6 +1594,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
model=self.model,
|
model=self.model,
|
||||||
pk_set=missing_target_ids,
|
pk_set=missing_target_ids,
|
||||||
using=db,
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
# Add the ones that aren't there already.
|
# Add the ones that aren't there already.
|
||||||
self.through._default_manager.using(db).bulk_create(
|
self.through._default_manager.using(db).bulk_create(
|
||||||
|
|
@ -1584,9 +1620,12 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
model=self.model,
|
model=self.model,
|
||||||
pk_set=missing_target_ids,
|
pk_set=missing_target_ids,
|
||||||
using=db,
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _remove_items(self, source_field_name, target_field_name, *objs):
|
def _remove_items(
|
||||||
|
self, source_field_name, target_field_name, *objs, using=None, raw=False
|
||||||
|
):
|
||||||
# source_field_name: the PK colname in join table for the source
|
# source_field_name: the PK colname in join table for the source
|
||||||
# object target_field_name: the PK colname in join table for the
|
# object target_field_name: the PK colname in join table for the
|
||||||
# target object *objs - objects to remove. Either object instances,
|
# target object *objs - objects to remove. Either object instances,
|
||||||
|
|
@ -1603,7 +1642,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
else:
|
else:
|
||||||
old_ids.add(obj)
|
old_ids.add(obj)
|
||||||
|
|
||||||
db = router.db_for_write(self.through, instance=self.instance)
|
db = using or router.db_for_write(self.through, instance=self.instance)
|
||||||
with transaction.atomic(using=db, savepoint=False):
|
with transaction.atomic(using=db, savepoint=False):
|
||||||
# Send a signal to the other end if need be.
|
# Send a signal to the other end if need be.
|
||||||
signals.m2m_changed.send(
|
signals.m2m_changed.send(
|
||||||
|
|
@ -1614,6 +1653,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
model=self.model,
|
model=self.model,
|
||||||
pk_set=old_ids,
|
pk_set=old_ids,
|
||||||
using=db,
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
target_model_qs = super().get_queryset()
|
target_model_qs = super().get_queryset()
|
||||||
if target_model_qs._has_filters():
|
if target_model_qs._has_filters():
|
||||||
|
|
@ -1633,6 +1673,7 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
|
||||||
model=self.model,
|
model=self.model,
|
||||||
pk_set=old_ids,
|
pk_set=old_ids,
|
||||||
using=db,
|
using=db,
|
||||||
|
raw=raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
return ManyRelatedManager
|
return ManyRelatedManager
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,15 @@ Arguments sent with this signal:
|
||||||
``using``
|
``using``
|
||||||
The database alias being used.
|
The database alias being used.
|
||||||
|
|
||||||
|
``raw``
|
||||||
|
|
||||||
|
.. versionadded:: 6.1
|
||||||
|
|
||||||
|
A boolean; ``True`` if the model is saved exactly as presented
|
||||||
|
(i.e. when loading a :ref:`fixture <fixtures-explanation>`). One should not
|
||||||
|
query/modify other records in the database as the database might not be in
|
||||||
|
a consistent state yet.
|
||||||
|
|
||||||
For example, if a ``Pizza`` can have multiple ``Topping`` objects, modeled
|
For example, if a ``Pizza`` can have multiple ``Topping`` objects, modeled
|
||||||
like this::
|
like this::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,9 @@ Logging
|
||||||
Management Commands
|
Management Commands
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The :djadmin:`loaddata` command now calls
|
||||||
|
:data:`~django.db.models.signals.m2m_changed` signals with ``raw=True`` when
|
||||||
|
loading fixtures.
|
||||||
|
|
||||||
Migrations
|
Migrations
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,9 @@ How fixtures are saved to the database
|
||||||
|
|
||||||
When fixture files are processed, the data is saved to the database as is.
|
When fixture files are processed, the data is saved to the database as is.
|
||||||
Model defined :meth:`~django.db.models.Model.save` methods are not called, and
|
Model defined :meth:`~django.db.models.Model.save` methods are not called, and
|
||||||
any :data:`~django.db.models.signals.pre_save` or
|
any :data:`~django.db.models.signals.pre_save`,
|
||||||
:data:`~django.db.models.signals.post_save` signals will be called with
|
:data:`~django.db.models.signals.post_save`, or
|
||||||
|
:data:`~django.db.models.signals.m2m_changed` signals will be called with
|
||||||
``raw=True`` since the instance only contains attributes that are local to the
|
``raw=True`` since the instance only contains attributes that are local to the
|
||||||
model. You may, for example, want to disable handlers that access
|
model. You may, for example, want to disable handlers that access
|
||||||
related fields that aren't present during fixture loading and would otherwise
|
related fields that aren't present during fixture loading and would otherwise
|
||||||
|
|
@ -163,6 +164,10 @@ You could also write a decorator to encapsulate this logic::
|
||||||
Just be aware that this logic will disable the signals whenever fixtures are
|
Just be aware that this logic will disable the signals whenever fixtures are
|
||||||
deserialized, not just during :djadmin:`loaddata`.
|
deserialized, not just during :djadmin:`loaddata`.
|
||||||
|
|
||||||
|
.. versionchanged:: 6.1
|
||||||
|
|
||||||
|
The ``raw`` argument was added to ``m2m_changed`` signals.
|
||||||
|
|
||||||
Compressed fixtures
|
Compressed fixtures
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": kwargs["action"],
|
"action": kwargs["action"],
|
||||||
"reverse": kwargs["reverse"],
|
"reverse": kwargs["reverse"],
|
||||||
"model": kwargs["model"],
|
"model": kwargs["model"],
|
||||||
|
"raw": kwargs["raw"],
|
||||||
}
|
}
|
||||||
if kwargs["pk_set"]:
|
if kwargs["pk_set"]:
|
||||||
message["objects"] = list(
|
message["objects"] = list(
|
||||||
|
|
@ -114,6 +115,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors, self.engine, self.wheelset],
|
"objects": [self.doors, self.engine, self.wheelset],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -123,6 +125,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors, self.engine, self.wheelset],
|
"objects": [self.doors, self.engine, self.wheelset],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -136,6 +139,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.bmw, self.toyota],
|
"objects": [self.bmw, self.toyota],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -145,6 +149,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.bmw, self.toyota],
|
"objects": [self.bmw, self.toyota],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -163,6 +168,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_remove",
|
"action": "pre_remove",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.airbag, self.engine],
|
"objects": [self.airbag, self.engine],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -170,6 +176,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_remove",
|
"action": "post_remove",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.airbag, self.engine],
|
"objects": [self.airbag, self.engine],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -188,6 +195,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.airbag, self.sunroof],
|
"objects": [self.airbag, self.sunroof],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -197,6 +205,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.airbag, self.sunroof],
|
"objects": [self.airbag, self.sunroof],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -210,6 +219,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.bmw, self.toyota],
|
"objects": [self.bmw, self.toyota],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -219,6 +229,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.bmw, self.toyota],
|
"objects": [self.bmw, self.toyota],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -237,6 +248,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_remove",
|
"action": "pre_remove",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.vw],
|
"objects": [self.vw],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -244,6 +256,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_remove",
|
"action": "post_remove",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.vw],
|
"objects": [self.vw],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -261,12 +274,14 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_clear",
|
"action": "pre_clear",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"instance": self.vw,
|
"instance": self.vw,
|
||||||
"action": "post_clear",
|
"action": "post_clear",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -283,12 +298,14 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_clear",
|
"action": "pre_clear",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"instance": self.doors,
|
"instance": self.doors,
|
||||||
"action": "post_clear",
|
"action": "post_clear",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -306,12 +323,14 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_clear",
|
"action": "pre_clear",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"instance": self.airbag,
|
"instance": self.airbag,
|
||||||
"action": "post_clear",
|
"action": "post_clear",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -330,6 +349,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [p6],
|
"objects": [p6],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -339,6 +359,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [p6],
|
"objects": [p6],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -352,6 +373,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_remove",
|
"action": "pre_remove",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [p6],
|
"objects": [p6],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -361,6 +383,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_remove",
|
"action": "post_remove",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [p6],
|
"objects": [p6],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -370,6 +393,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors, self.engine, self.wheelset],
|
"objects": [self.doors, self.engine, self.wheelset],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -379,6 +403,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors, self.engine, self.wheelset],
|
"objects": [self.doors, self.engine, self.wheelset],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -397,6 +422,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_clear",
|
"action": "pre_clear",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
expected_messages.append(
|
expected_messages.append(
|
||||||
|
|
@ -405,6 +431,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_clear",
|
"action": "post_clear",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
expected_messages.append(
|
expected_messages.append(
|
||||||
|
|
@ -413,6 +440,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors, self.engine, self.wheelset],
|
"objects": [self.doors, self.engine, self.wheelset],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -422,6 +450,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors, self.engine, self.wheelset],
|
"objects": [self.doors, self.engine, self.wheelset],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -435,6 +464,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_remove",
|
"action": "pre_remove",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.engine],
|
"objects": [self.engine],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -444,6 +474,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_remove",
|
"action": "post_remove",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.engine],
|
"objects": [self.engine],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -464,6 +495,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors],
|
"objects": [self.doors],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -473,6 +505,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Part,
|
"model": Part,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.doors],
|
"objects": [self.doors],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -485,6 +518,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [c4b],
|
"objects": [c4b],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -494,6 +528,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Car,
|
"model": Car,
|
||||||
|
"raw": False,
|
||||||
"objects": [c4b],
|
"objects": [c4b],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -519,6 +554,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Person,
|
"model": Person,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.bob, self.chuck],
|
"objects": [self.bob, self.chuck],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -526,6 +562,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Person,
|
"model": Person,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.bob, self.chuck],
|
"objects": [self.bob, self.chuck],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -542,6 +579,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Person,
|
"model": Person,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.daisy],
|
"objects": [self.daisy],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -549,6 +587,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": False,
|
"reverse": False,
|
||||||
"model": Person,
|
"model": Person,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.daisy],
|
"objects": [self.daisy],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
@ -565,6 +604,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "pre_add",
|
"action": "pre_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Person,
|
"model": Person,
|
||||||
|
"raw": False,
|
||||||
"objects": [self.alice, self.bob],
|
"objects": [self.alice, self.bob],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -572,6 +612,89 @@ class ManyToManySignalsTest(TestCase):
|
||||||
"action": "post_add",
|
"action": "post_add",
|
||||||
"reverse": True,
|
"reverse": True,
|
||||||
"model": Person,
|
"model": Person,
|
||||||
|
"raw": False,
|
||||||
|
"objects": [self.alice, self.bob],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_m2m_relations_set_base_raw(self):
|
||||||
|
self.chuck.idols.add(self.daisy)
|
||||||
|
self._initialize_signal_person()
|
||||||
|
self.chuck.idols.set_base([self.alice, self.bob], raw=True)
|
||||||
|
self.assertEqual(
|
||||||
|
self.m2m_changed_messages,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "pre_remove",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
"objects": [self.daisy],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "post_remove",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
"objects": [self.daisy],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "pre_add",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
"objects": [self.alice, self.bob],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "post_add",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
"objects": [self.alice, self.bob],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_m2m_relations_set_base_raw_clear(self):
|
||||||
|
self.chuck.idols.set([self.daisy, self.bob])
|
||||||
|
self._initialize_signal_person()
|
||||||
|
self.chuck.idols.set_base([self.alice, self.bob], clear=True, raw=True)
|
||||||
|
self.assertEqual(
|
||||||
|
self.m2m_changed_messages,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "pre_clear",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "post_clear",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "pre_add",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
|
"objects": [self.alice, self.bob],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"instance": self.chuck,
|
||||||
|
"action": "post_add",
|
||||||
|
"reverse": True,
|
||||||
|
"model": Person,
|
||||||
|
"raw": True,
|
||||||
"objects": [self.alice, self.bob],
|
"objects": [self.alice, self.bob],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ def fk_create(pk, klass, data):
|
||||||
def m2m_create(pk, klass, data):
|
def m2m_create(pk, klass, data):
|
||||||
instance = klass(id=pk)
|
instance = klass(id=pk)
|
||||||
models.Model.save_base(instance, raw=True)
|
models.Model.save_base(instance, raw=True)
|
||||||
instance.data.set(data)
|
instance.data.set_base(data, raw=True)
|
||||||
return [instance]
|
return [instance]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue