Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

queryset-refactor: Reorganised Model.save() to differentiate between …

…public and private parameters. Refs #6741.

This means subclasses can override save() without needing to worry about
passing around the internal parameters (an issue for subclassable models, which
would have meant every model, since you don't know when somebody will subclass
your model).

Slightly backwards incompatible, since it moves "raw" back to being part of the
internal API (it's only needed by the serializer and was intended to be
internal use only). If external code really needs this, they can call
Model.save_base() and pass in raw there.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7221 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 3176bebffd7a7e3ba131da2d1b3f9776e159f0a8 1 parent 6195760
@malcolmt malcolmt authored
View
6 django/core/serializers/base.py
@@ -162,12 +162,12 @@ def __repr__(self):
return "<DeserializedObject: %s>" % smart_str(self.object)
def save(self, save_m2m=True):
- # 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.
- # This ensures that the data that is deserialized is literally
+ # This ensures that the data that is deserialized is literally
# what came from the file, not post-processed by pre_save/save
# methods.
- models.Model.save(self.object, raw=True)
+ models.Model.save_base(self.object, raw=True)
if self.m2m_data and save_m2m:
for accessor_name, object_list in self.m2m_data.items():
setattr(self.object, accessor_name, object_list)
View
26 django/db/models/base.py
@@ -253,19 +253,34 @@ def __init__(self, *args, **kwargs):
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
- def save(self, raw=False, cls=None):
+ def save(self):
+ """
+ Save the current instance. Override this in a subclass if you want to
+ control the saving process.
+ """
+ self.save_base()
+
+ save.alters_data = True
+
+ def save_base(self, raw=False, cls=None):
+ """
+ Does the heavy-lifting involved in saving. Subclasses shouldn't need to
+ override this method. It's separate from save() in order to hide the
+ need for overrides of save() to pass around internal-only parameters
+ ('raw' and 'cls').
+ """
if not cls:
- dispatcher.send(signal=signals.pre_save, sender=self.__class__,
- instance=self, raw=raw)
cls = self.__class__
meta = self._meta
signal = True
+ dispatcher.send(signal=signals.pre_save, sender=self.__class__,
+ instance=self, raw=raw)
else:
meta = cls._meta
signal = False
for parent, field in meta.parents.items():
- self.save(raw, parent)
+ self.save_base(raw, parent)
setattr(self, field.attname, self._get_pk_val(parent._meta))
non_pks = [f for f in meta.local_fields if not f.primary_key]
@@ -311,12 +326,9 @@ def save(self, raw=False, cls=None):
transaction.commit_unless_managed()
if signal:
- # Run any post-save hooks.
dispatcher.send(signal=signals.post_save, sender=self.__class__,
instance=self, created=(not record_exists), raw=raw)
- save.alters_data = True
-
def validate(self):
"""
First coerces all fields on this instance to their proper Python types.
View
31 docs/db-api.txt
@@ -160,37 +160,6 @@ When you save an object, Django performs the following steps:
is used to provide notification that an object has been successfully
saved. (These signals are not yet documented.)
-Raw saves
-~~~~~~~~~
-
-**New in Django development version**
-
-The pre-processing step (#2 in the previous section) is useful, but it modifies
-the data stored in a field. This can cause problems if you're relying upon the
-data you provide being used as-is.
-
-For example, if you're setting up conditions for a test, you'll want the test
-conditions to be repeatable. If pre-processing is performed, the data used
-to specify test conditions may be modified, changing the conditions for the
-test each time the test is run.
-
-In cases such as this, you need to prevent pre-processing from being performed
-when you save an object. To do this, you can invoke a **raw save** by passing
-``raw=True`` as an argument to the ``save()`` method::
-
- b4.save(raw=True) # Save object, but do no pre-processing
-
-A raw save skips the usual data pre-processing that is performed during the
-save. All other steps in the save (pre-save signal, data preparation, data
-insertion, and post-save signal) are performed as normal.
-
-.. admonition:: When to use a raw save
-
- Generally speaking, you shouldn't need to use a raw save. Disabling field
- pre-processing is an extraordinary measure that should only be required
- in extraordinary circumstances, such as setting up reliable test
- conditions.
-
Saving changes to objects
=========================
View
3  tests/modeltests/signals/models.py
@@ -66,7 +66,8 @@ def post_delete_test(sender, instance, **kwargs):
post_save signal, Tom Smith
Is updated
->>> p1.save(raw=True)
+# Calling an internal method purely so that we can trigger a "raw" save.
+>>> p1.save_base(raw=True)
pre_save_nokwargs signal
pre_save signal, Tom Smith
Is raw
View
16 tests/regressiontests/serializers_regress/tests.py
@@ -31,13 +31,13 @@
def data_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data
- models.Model.save(instance, raw=True)
+ models.Model.save_base(instance, raw=True)
return instance
def generic_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data[0]
- models.Model.save(instance, raw=True)
+ models.Model.save_base(instance, raw=True)
for tag in data[1:]:
instance.tags.create(data=tag)
return instance
@@ -45,25 +45,25 @@ def generic_create(pk, klass, data):
def fk_create(pk, klass, data):
instance = klass(id=pk)
setattr(instance, 'data_id', data)
- models.Model.save(instance, raw=True)
+ models.Model.save_base(instance, raw=True)
return instance
def m2m_create(pk, klass, data):
instance = klass(id=pk)
- models.Model.save(instance, raw=True)
+ models.Model.save_base(instance, raw=True)
instance.data = data
return instance
def o2o_create(pk, klass, data):
instance = klass()
instance.data_id = data
- models.Model.save(instance, raw=True)
+ models.Model.save_base(instance, raw=True)
return instance
def pk_create(pk, klass, data):
instance = klass()
instance.data = data
- models.Model.save(instance, raw=True)
+ models.Model.save_base(instance, raw=True)
return instance
# A set of functions that can be used to compare
@@ -309,7 +309,7 @@ def fieldsTest(format, self):
management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
- obj.save(raw=True)
+ obj.save()
# Serialize then deserialize the test database
serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
@@ -325,7 +325,7 @@ def streamTest(format, self):
management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
- obj.save(raw=True)
+ obj.save()
# Serialize the test database to a stream
stream = StringIO()
Please sign in to comment.
Something went wrong with that request. Please try again.