Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #4459 -- Added 'raw' argument to save method, to override any p…

…re-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
  • Loading branch information...
commit 2d6d20def70dacf4ea783e5a0c8a72266e603bb7 1 parent 090aa52
Russell Keith-Magee authored July 12, 2007
7  django/core/serializers/base.py
@@ -158,7 +158,12 @@ def __repr__(self):
158 158
         return "<DeserializedObject: %s>" % smart_str(self.object)
159 159
 
160 160
     def save(self, save_m2m=True):
161  
-        self.object.save()
  161
+        # Call save on the Model baseclass directly. This bypasses any 
  162
+        # model-defined save. The save is also forced to be raw.
  163
+        # This ensures that the data that is deserialized is literally 
  164
+        # what came from the file, not post-processed by pre_save/save
  165
+        # methods.
  166
+        models.Model.save(self.object, raw=True)
162 167
         if self.m2m_data and save_m2m:
163 168
             for accessor_name, object_list in self.m2m_data.items():
164 169
                 setattr(self.object, accessor_name, object_list)
8  django/db/models/base.py
@@ -201,7 +201,7 @@ def _prepare(cls):
201 201
 
202 202
     _prepare = classmethod(_prepare)
203 203
 
204  
-    def save(self):
  204
+    def save(self, raw=False):
205 205
         dispatcher.send(signal=signals.pre_save, sender=self.__class__, instance=self)
206 206
 
207 207
         non_pks = [f for f in self._meta.fields if not f.primary_key]
@@ -218,7 +218,7 @@ def save(self):
218 218
                 self._meta.pk.get_db_prep_lookup('exact', pk_val))
219 219
             # If it does already exist, do an UPDATE.
220 220
             if cursor.fetchone()[0] > 0:
221  
-                db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks]
  221
+                db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
222 222
                 if db_values:
223 223
                     cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
224 224
                         (backend.quote_name(self._meta.db_table),
@@ -229,11 +229,11 @@ def save(self):
229 229
                 record_exists = False
230 230
         if not pk_set or not record_exists:
231 231
             field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
232  
-            db_values = [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
  232
+            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)]
233 233
             # If the PK has been manually set, respect that.
234 234
             if pk_set:
235 235
                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
236  
-                db_values += [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
  236
+                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)]
237 237
             placeholders = ['%s'] * len(field_names)
238 238
             if self._meta.order_with_respect_to:
239 239
                 field_names.append(backend.quote_name('_order'))
17  docs/db-api.txt
@@ -118,6 +118,23 @@ happens.
118 118
 Explicitly specifying auto-primary-key values is mostly useful for bulk-saving
119 119
 objects, when you're confident you won't have primary-key collision.
120 120
 
  121
+Raw saves
  122
+---------
  123
+
  124
+When you save an Django object, some pre-processing will occur on the the data
  125
+that is in the object. For example, if your model has a ``DateField`` with 
  126
+``auto_now=True`` set, the pre-save phase will alter the data in the object 
  127
+to ensure that the date field contains the current date stamp.
  128
+
  129
+Although these automated changes can be very useful, there will be times when
  130
+you just want to save the data as-is. In these cases, you can invoke a *Raw Save* 
  131
+by passing ``raw=True`` as an argument to the ``save()`` method::
  132
+
  133
+    b4.save(raw=True)  # Saves object, but does no pre-processing
  134
+
  135
+A raw save saves all the data in your object, but performs no pre-save processing 
  136
+on the data in the object. 
  137
+
121 138
 Saving changes to objects
122 139
 =========================
123 140
 
13  tests/regressiontests/serializers_regress/models.py
@@ -209,3 +209,16 @@ class ComplexModel(models.Model):
209 209
     field1 = models.CharField(maxlength=10)
210 210
     field2 = models.CharField(maxlength=10)
211 211
     field3 = models.CharField(maxlength=10)
  212
+
  213
+# Tests for handling fields with pre_save functions, or
  214
+# models with save functions that modify data
  215
+class AutoNowDateTimeData(models.Model):
  216
+    data = models.DateTimeField(null=True, auto_now=True)
  217
+
  218
+class ModifyingSaveData(models.Model):
  219
+    data = models.IntegerField(null=True)
  220
+
  221
+    def save(self):
  222
+        "A save method that modifies the data in the object"
  223
+        self.data = 666
  224
+        super(ModifyingSaveData, self).save(raw)
24  tests/regressiontests/serializers_regress/tests.py
@@ -24,17 +24,20 @@
24 24
     from django.utils import _decimal as decimal
25 25
 
26 26
 # A set of functions that can be used to recreate
27  
-# test data objects of various kinds
  27
+# test data objects of various kinds.
  28
+# The save method is a raw base model save, to make
  29
+# sure that the data in the database matches the
  30
+# exact test case.
28 31
 def data_create(pk, klass, data):
29 32
     instance = klass(id=pk)
30 33
     instance.data = data
31  
-    instance.save()
  34
+    models.Model.save(instance, raw=True)
32 35
     return instance
33 36
 
34 37
 def generic_create(pk, klass, data):
35 38
     instance = klass(id=pk)
36 39
     instance.data = data[0]
37  
-    instance.save()
  40
+    models.Model.save(instance, raw=True)
38 41
     for tag in data[1:]:
39 42
         instance.tags.create(data=tag)
40 43
     return instance
@@ -42,25 +45,25 @@ def generic_create(pk, klass, data):
42 45
 def fk_create(pk, klass, data):
43 46
     instance = klass(id=pk)
44 47
     setattr(instance, 'data_id', data)
45  
-    instance.save()
  48
+    models.Model.save(instance, raw=True)
46 49
     return instance
47 50
 
48 51
 def m2m_create(pk, klass, data):
49 52
     instance = klass(id=pk)
50  
-    instance.save()
  53
+    models.Model.save(instance, raw=True)
51 54
     instance.data = data
52 55
     return instance
53 56
 
54 57
 def o2o_create(pk, klass, data):
55 58
     instance = klass()
56 59
     instance.data_id = data
57  
-    instance.save()
  60
+    models.Model.save(instance, raw=True)
58 61
     return instance
59 62
 
60 63
 def pk_create(pk, klass, data):
61 64
     instance = klass()
62 65
     instance.data = data
63  
-    instance.save()
  66
+    models.Model.save(instance, raw=True)
64 67
     return instance
65 68
 
66 69
 # A set of functions that can be used to compare
@@ -249,6 +252,9 @@ def pk_compare(testcase, pk, klass, data):
249 252
 #    (pk_obj, 770, TimePKData, datetime.time(10,42,37)),
250 253
     (pk_obj, 780, USStatePKData, "MA"),
251 254
 #     (pk_obj, 790, XMLPKData, "<foo></foo>"),
  255
+
  256
+    (data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006,6,16,10,42,37)),
  257
+    (data_obj, 810, ModifyingSaveData, 42),
252 258
 ]
253 259
 
254 260
 # Because Oracle treats the empty string as NULL, Oracle is expected to fail
@@ -303,7 +309,7 @@ def fieldsTest(format, self):
303 309
     management.flush(verbosity=0, interactive=False)
304 310
 
305 311
     obj = ComplexModel(field1='first',field2='second',field3='third')
306  
-    obj.save()
  312
+    obj.save(raw=True)
307 313
 
308 314
     # Serialize then deserialize the test database
309 315
     serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
@@ -319,7 +325,7 @@ def streamTest(format, self):
319 325
     management.flush(verbosity=0, interactive=False)
320 326
 
321 327
     obj = ComplexModel(field1='first',field2='second',field3='third')
322  
-    obj.save()
  328
+    obj.save(raw=True)
323 329
 
324 330
     # Serialize the test database to a stream
325 331
     stream = StringIO()

0 notes on commit 2d6d20d

Please sign in to comment.
Something went wrong with that request. Please try again.