[1.5.X] Fixed #20136 - Fixed and expanded the docs for loaddata and model signals.

Thanks brandon@ and Anssi for the report.

Backport of 2c62a509de from master
This commit is contained in:
Tim Graham 2013-05-11 19:34:02 -04:00
parent 13de270bd5
commit 18a2fb1907
2 changed files with 41 additions and 4 deletions

View File

@ -161,9 +161,7 @@ class DeserializedObject(object):
def save(self, save_m2m=True, using=None):
# Call save on the Model baseclass directly. This bypasses any
# model-defined save. The save is also forced to be raw.
# This ensures that the data that is deserialized is literally
# what came from the file, not post-processed by pre_save/save
# methods.
# raw=True is passed to any pre/post_save signals.
models.Model.save_base(self.object, using=using, raw=True)
if self.m2m_data and save_m2m:
for accessor_name, object_list in self.m2m_data.items():

View File

@ -344,7 +344,46 @@ application, ``<dirname>/foo/bar/mydata.json`` for each directory in
:setting:`FIXTURE_DIRS`, and the literal path ``foo/bar/mydata.json``.
When fixture files are processed, the data is saved to the database as is.
Model defined ``save`` methods and ``pre_save`` signals are not called.
Model defined :meth:`~django.db.models.Model.save` methods are not called, and
any :data:`~django.db.models.signals.pre_save` or
:data:`~django.db.models.signals.post_save` signals will be called with
``raw=True`` since the instance only contains attributes that are local to the
model. You may, for example, want to disable handlers that access
related fields that aren't present during fixture loading and would otherwise
raise an exception::
from django.db.models.signals import post_save
from .models import MyModel
def my_handler(**kwargs):
# disable the handler during fixture loading
if kwargs['raw']:
return
...
post_save.connect(my_handler, sender=MyModel)
You could also write a simple decorator to encapsulate this logic::
from functools import wraps
def disable_for_loaddata(signal_handler):
"""
Decorator that turns off signal handlers when loading fixture data.
"""
@wraps(signal_handler)
def wrapper(*args, **kwargs):
if kwargs['raw']:
return
signal_handler(*args, **kwargs)
return wrapper
@disable_for_loaddata
def my_handler(**kwargs):
...
Just be aware that this logic will disable the signals whenever fixtures are
deserialized, not just during ``loaddata``.
Note that the order in which fixture files are processed is undefined. However,
all fixture data is installed as a single transaction, so data in