mirror of
https://github.com/django/django.git
synced 2025-03-31 19:46:42 +00:00
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:
parent
090aa5210e
commit
2d6d20def7
@ -158,7 +158,12 @@ class DeserializedObject(object):
|
|||||||
return "<DeserializedObject: %s>" % smart_str(self.object)
|
return "<DeserializedObject: %s>" % smart_str(self.object)
|
||||||
|
|
||||||
def save(self, save_m2m=True):
|
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:
|
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():
|
||||||
setattr(self.object, accessor_name, object_list)
|
setattr(self.object, accessor_name, object_list)
|
||||||
|
@ -201,7 +201,7 @@ class Model(object):
|
|||||||
|
|
||||||
_prepare = classmethod(_prepare)
|
_prepare = classmethod(_prepare)
|
||||||
|
|
||||||
def save(self):
|
def save(self, raw=False):
|
||||||
dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
|
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]
|
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))
|
self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
||||||
# If it does already exist, do an UPDATE.
|
# If it does already exist, do an UPDATE.
|
||||||
if cursor.fetchone()[0] > 0:
|
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:
|
if db_values:
|
||||||
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
||||||
(backend.quote_name(self._meta.db_table),
|
(backend.quote_name(self._meta.db_table),
|
||||||
@ -229,11 +229,11 @@ class Model(object):
|
|||||||
record_exists = False
|
record_exists = False
|
||||||
if not pk_set or not record_exists:
|
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)]
|
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 the PK has been manually set, respect that.
|
||||||
if pk_set:
|
if pk_set:
|
||||||
field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
|
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)
|
placeholders = ['%s'] * len(field_names)
|
||||||
if self._meta.order_with_respect_to:
|
if self._meta.order_with_respect_to:
|
||||||
field_names.append(backend.quote_name('_order'))
|
field_names.append(backend.quote_name('_order'))
|
||||||
|
@ -118,6 +118,23 @@ happens.
|
|||||||
Explicitly specifying auto-primary-key values is mostly useful for bulk-saving
|
Explicitly specifying auto-primary-key values is mostly useful for bulk-saving
|
||||||
objects, when you're confident you won't have primary-key collision.
|
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
|
Saving changes to objects
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
@ -209,3 +209,16 @@ class ComplexModel(models.Model):
|
|||||||
field1 = models.CharField(maxlength=10)
|
field1 = models.CharField(maxlength=10)
|
||||||
field2 = models.CharField(maxlength=10)
|
field2 = models.CharField(maxlength=10)
|
||||||
field3 = 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)
|
||||||
|
@ -24,17 +24,20 @@ except ImportError:
|
|||||||
from django.utils import _decimal as decimal
|
from django.utils import _decimal as decimal
|
||||||
|
|
||||||
# A set of functions that can be used to recreate
|
# 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):
|
def data_create(pk, klass, data):
|
||||||
instance = klass(id=pk)
|
instance = klass(id=pk)
|
||||||
instance.data = data
|
instance.data = data
|
||||||
instance.save()
|
models.Model.save(instance, raw=True)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def generic_create(pk, klass, data):
|
def generic_create(pk, klass, data):
|
||||||
instance = klass(id=pk)
|
instance = klass(id=pk)
|
||||||
instance.data = data[0]
|
instance.data = data[0]
|
||||||
instance.save()
|
models.Model.save(instance, raw=True)
|
||||||
for tag in data[1:]:
|
for tag in data[1:]:
|
||||||
instance.tags.create(data=tag)
|
instance.tags.create(data=tag)
|
||||||
return instance
|
return instance
|
||||||
@ -42,25 +45,25 @@ def generic_create(pk, klass, data):
|
|||||||
def fk_create(pk, klass, data):
|
def fk_create(pk, klass, data):
|
||||||
instance = klass(id=pk)
|
instance = klass(id=pk)
|
||||||
setattr(instance, 'data_id', data)
|
setattr(instance, 'data_id', data)
|
||||||
instance.save()
|
models.Model.save(instance, raw=True)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def m2m_create(pk, klass, data):
|
def m2m_create(pk, klass, data):
|
||||||
instance = klass(id=pk)
|
instance = klass(id=pk)
|
||||||
instance.save()
|
models.Model.save(instance, raw=True)
|
||||||
instance.data = data
|
instance.data = data
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def o2o_create(pk, klass, data):
|
def o2o_create(pk, klass, data):
|
||||||
instance = klass()
|
instance = klass()
|
||||||
instance.data_id = data
|
instance.data_id = data
|
||||||
instance.save()
|
models.Model.save(instance, raw=True)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def pk_create(pk, klass, data):
|
def pk_create(pk, klass, data):
|
||||||
instance = klass()
|
instance = klass()
|
||||||
instance.data = data
|
instance.data = data
|
||||||
instance.save()
|
models.Model.save(instance, raw=True)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
# A set of functions that can be used to compare
|
# 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, 770, TimePKData, datetime.time(10,42,37)),
|
||||||
(pk_obj, 780, USStatePKData, "MA"),
|
(pk_obj, 780, USStatePKData, "MA"),
|
||||||
# (pk_obj, 790, XMLPKData, "<foo></foo>"),
|
# (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
|
# 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)
|
management.flush(verbosity=0, interactive=False)
|
||||||
|
|
||||||
obj = ComplexModel(field1='first',field2='second',field3='third')
|
obj = ComplexModel(field1='first',field2='second',field3='third')
|
||||||
obj.save()
|
obj.save(raw=True)
|
||||||
|
|
||||||
# Serialize then deserialize the test database
|
# Serialize then deserialize the test database
|
||||||
serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
|
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)
|
management.flush(verbosity=0, interactive=False)
|
||||||
|
|
||||||
obj = ComplexModel(field1='first',field2='second',field3='third')
|
obj = ComplexModel(field1='first',field2='second',field3='third')
|
||||||
obj.save()
|
obj.save(raw=True)
|
||||||
|
|
||||||
# Serialize the test database to a stream
|
# Serialize the test database to a stream
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user