Fixed #4459 -- Added 'raw' argument to save method, to override any pre-save processing, and modified serializers to use a raw-save. This enables serialization of DateFields with auto_now/auto_now_add. Also modified serializers to invoke save() directly on the model baseclass, to avoid any (potentially order-dependent, data modifying) behavior in a custom save() method.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5658 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2007-07-12 07:45:35 +00:00
parent 090aa5210e
commit 2d6d20def7
5 changed files with 55 additions and 14 deletions

View File

@ -158,7 +158,12 @@ class DeserializedObject(object):
return "<DeserializedObject: %s>" % smart_str(self.object)
def save(self, save_m2m=True):
self.object.save()
# 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.
models.Model.save(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 File

@ -201,7 +201,7 @@ class Model(object):
_prepare = classmethod(_prepare)
def save(self):
def save(self, raw=False):
dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
non_pks = [f for f in self._meta.fields if not f.primary_key]
@ -218,7 +218,7 @@ class Model(object):
self._meta.pk.get_db_prep_lookup('exact', pk_val))
# If it does already exist, do an UPDATE.
if cursor.fetchone()[0] > 0:
db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks]
db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
if db_values:
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
(backend.quote_name(self._meta.db_table),
@ -229,11 +229,11 @@ class Model(object):
record_exists = False
if not pk_set or not record_exists:
field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
db_values = [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
# If the PK has been manually set, respect that.
if pk_set:
field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
db_values += [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
placeholders = ['%s'] * len(field_names)
if self._meta.order_with_respect_to:
field_names.append(backend.quote_name('_order'))

View File

@ -118,6 +118,23 @@ happens.
Explicitly specifying auto-primary-key values is mostly useful for bulk-saving
objects, when you're confident you won't have primary-key collision.
Raw saves
---------
When you save an Django object, some pre-processing will occur on the the data
that is in the object. For example, if your model has a ``DateField`` with
``auto_now=True`` set, the pre-save phase will alter the data in the object
to ensure that the date field contains the current date stamp.
Although these automated changes can be very useful, there will be times when
you just want to save the data as-is. In these cases, you can invoke a *Raw Save*
by passing ``raw=True`` as an argument to the ``save()`` method::
b4.save(raw=True) # Saves object, but does no pre-processing
A raw save saves all the data in your object, but performs no pre-save processing
on the data in the object.
Saving changes to objects
=========================

View File

@ -209,3 +209,16 @@ class ComplexModel(models.Model):
field1 = models.CharField(maxlength=10)
field2 = models.CharField(maxlength=10)
field3 = models.CharField(maxlength=10)
# Tests for handling fields with pre_save functions, or
# models with save functions that modify data
class AutoNowDateTimeData(models.Model):
data = models.DateTimeField(null=True, auto_now=True)
class ModifyingSaveData(models.Model):
data = models.IntegerField(null=True)
def save(self):
"A save method that modifies the data in the object"
self.data = 666
super(ModifyingSaveData, self).save(raw)

View File

@ -24,17 +24,20 @@ except ImportError:
from django.utils import _decimal as decimal
# A set of functions that can be used to recreate
# test data objects of various kinds
# test data objects of various kinds.
# The save method is a raw base model save, to make
# sure that the data in the database matches the
# exact test case.
def data_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data
instance.save()
models.Model.save(instance, raw=True)
return instance
def generic_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data[0]
instance.save()
models.Model.save(instance, raw=True)
for tag in data[1:]:
instance.tags.create(data=tag)
return instance
@ -42,25 +45,25 @@ def generic_create(pk, klass, data):
def fk_create(pk, klass, data):
instance = klass(id=pk)
setattr(instance, 'data_id', data)
instance.save()
models.Model.save(instance, raw=True)
return instance
def m2m_create(pk, klass, data):
instance = klass(id=pk)
instance.save()
models.Model.save(instance, raw=True)
instance.data = data
return instance
def o2o_create(pk, klass, data):
instance = klass()
instance.data_id = data
instance.save()
models.Model.save(instance, raw=True)
return instance
def pk_create(pk, klass, data):
instance = klass()
instance.data = data
instance.save()
models.Model.save(instance, raw=True)
return instance
# A set of functions that can be used to compare
@ -249,6 +252,9 @@ The end."""),
# (pk_obj, 770, TimePKData, datetime.time(10,42,37)),
(pk_obj, 780, USStatePKData, "MA"),
# (pk_obj, 790, XMLPKData, "<foo></foo>"),
(data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006,6,16,10,42,37)),
(data_obj, 810, ModifyingSaveData, 42),
]
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
@ -303,7 +309,7 @@ def fieldsTest(format, self):
management.flush(verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
obj.save()
obj.save(raw=True)
# Serialize then deserialize the test database
serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
@ -319,7 +325,7 @@ def streamTest(format, self):
management.flush(verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
obj.save()
obj.save(raw=True)
# Serialize the test database to a stream
stream = StringIO()