Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Malcolm Tredinnick authored March 11, 2008
6  django/core/serializers/base.py
@@ -162,12 +162,12 @@ def __repr__(self):
162 162
         return "<DeserializedObject: %s>" % smart_str(self.object)
163 163
 
164 164
     def save(self, save_m2m=True):
165  
-        # Call save on the Model baseclass directly. This bypasses any 
  165
+        # Call save on the Model baseclass directly. This bypasses any
166 166
         # model-defined save. The save is also forced to be raw.
167  
-        # This ensures that the data that is deserialized is literally 
  167
+        # This ensures that the data that is deserialized is literally
168 168
         # what came from the file, not post-processed by pre_save/save
169 169
         # methods.
170  
-        models.Model.save(self.object, raw=True)
  170
+        models.Model.save_base(self.object, raw=True)
171 171
         if self.m2m_data and save_m2m:
172 172
             for accessor_name, object_list in self.m2m_data.items():
173 173
                 setattr(self.object, accessor_name, object_list)
26  django/db/models/base.py
@@ -253,19 +253,34 @@ def __init__(self, *args, **kwargs):
253 253
                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
254 254
         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
255 255
 
256  
-    def save(self, raw=False, cls=None):
  256
+    def save(self):
  257
+        """
  258
+        Save the current instance. Override this in a subclass if you want to
  259
+        control the saving process.
  260
+        """
  261
+        self.save_base()
  262
+
  263
+    save.alters_data = True
  264
+
  265
+    def save_base(self, raw=False, cls=None):
  266
+        """
  267
+        Does the heavy-lifting involved in saving. Subclasses shouldn't need to
  268
+        override this method. It's separate from save() in order to hide the
  269
+        need for overrides of save() to pass around internal-only parameters
  270
+        ('raw' and 'cls').
  271
+        """
257 272
         if not cls:
258  
-            dispatcher.send(signal=signals.pre_save, sender=self.__class__,
259  
-                    instance=self, raw=raw)
260 273
             cls = self.__class__
261 274
             meta = self._meta
262 275
             signal = True
  276
+            dispatcher.send(signal=signals.pre_save, sender=self.__class__,
  277
+                    instance=self, raw=raw)
263 278
         else:
264 279
             meta = cls._meta
265 280
             signal = False
266 281
 
267 282
         for parent, field in meta.parents.items():
268  
-            self.save(raw, parent)
  283
+            self.save_base(raw, parent)
269 284
             setattr(self, field.attname, self._get_pk_val(parent._meta))
270 285
 
271 286
         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):
311 326
         transaction.commit_unless_managed()
312 327
 
313 328
         if signal:
314  
-            # Run any post-save hooks.
315 329
             dispatcher.send(signal=signals.post_save, sender=self.__class__,
316 330
                     instance=self, created=(not record_exists), raw=raw)
317 331
 
318  
-    save.alters_data = True
319  
-
320 332
     def validate(self):
321 333
         """
322 334
         First coerces all fields on this instance to their proper Python types.
31  docs/db-api.txt
@@ -160,37 +160,6 @@ When you save an object, Django performs the following steps:
160 160
        is used to provide notification that an object has been successfully
161 161
        saved. (These signals are not yet documented.)
162 162
 
163  
-Raw saves
164  
-~~~~~~~~~
165  
-
166  
-**New in Django development version**
167  
-
168  
-The pre-processing step (#2 in the previous section) is useful, but it modifies
169  
-the data stored in a field. This can cause problems if you're relying upon the
170  
-data you provide being used as-is.
171  
-
172  
-For example, if you're setting up conditions for a test, you'll want the test
173  
-conditions to be repeatable. If pre-processing is performed, the data used
174  
-to specify test conditions may be modified, changing the conditions for the
175  
-test each time the test is run.
176  
-
177  
-In cases such as this, you need to prevent pre-processing from being performed
178  
-when you save an object. To do this, you can invoke a **raw save**  by passing
179  
-``raw=True`` as an argument to the ``save()`` method::
180  
-
181  
-    b4.save(raw=True) # Save object, but do no pre-processing
182  
-
183  
-A raw save skips the usual data pre-processing that is performed during the
184  
-save. All other steps in the save (pre-save signal, data preparation, data
185  
-insertion, and post-save signal) are performed as normal.
186  
-
187  
-.. admonition:: When to use a raw save
188  
-
189  
-    Generally speaking, you shouldn't need to use a raw save. Disabling field
190  
-    pre-processing is an extraordinary measure that should only be required
191  
-    in extraordinary circumstances, such as setting up reliable test
192  
-    conditions.
193  
-
194 163
 Saving changes to objects
195 164
 =========================
196 165
 
3  tests/modeltests/signals/models.py
@@ -66,7 +66,8 @@ def post_delete_test(sender, instance, **kwargs):
66 66
 post_save signal, Tom Smith
67 67
 Is updated
68 68
 
69  
->>> p1.save(raw=True)
  69
+# Calling an internal method purely so that we can trigger a "raw" save.
  70
+>>> p1.save_base(raw=True)
70 71
 pre_save_nokwargs signal
71 72
 pre_save signal, Tom Smith
72 73
 Is raw
16  tests/regressiontests/serializers_regress/tests.py
@@ -31,13 +31,13 @@
31 31
 def data_create(pk, klass, data):
32 32
     instance = klass(id=pk)
33 33
     instance.data = data
34  
-    models.Model.save(instance, raw=True)
  34
+    models.Model.save_base(instance, raw=True)
35 35
     return instance
36 36
 
37 37
 def generic_create(pk, klass, data):
38 38
     instance = klass(id=pk)
39 39
     instance.data = data[0]
40  
-    models.Model.save(instance, raw=True)
  40
+    models.Model.save_base(instance, raw=True)
41 41
     for tag in data[1:]:
42 42
         instance.tags.create(data=tag)
43 43
     return instance
@@ -45,25 +45,25 @@ def generic_create(pk, klass, data):
45 45
 def fk_create(pk, klass, data):
46 46
     instance = klass(id=pk)
47 47
     setattr(instance, 'data_id', data)
48  
-    models.Model.save(instance, raw=True)
  48
+    models.Model.save_base(instance, raw=True)
49 49
     return instance
50 50
 
51 51
 def m2m_create(pk, klass, data):
52 52
     instance = klass(id=pk)
53  
-    models.Model.save(instance, raw=True)
  53
+    models.Model.save_base(instance, raw=True)
54 54
     instance.data = data
55 55
     return instance
56 56
 
57 57
 def o2o_create(pk, klass, data):
58 58
     instance = klass()
59 59
     instance.data_id = data
60  
-    models.Model.save(instance, raw=True)
  60
+    models.Model.save_base(instance, raw=True)
61 61
     return instance
62 62
 
63 63
 def pk_create(pk, klass, data):
64 64
     instance = klass()
65 65
     instance.data = data
66  
-    models.Model.save(instance, raw=True)
  66
+    models.Model.save_base(instance, raw=True)
67 67
     return instance
68 68
 
69 69
 # A set of functions that can be used to compare
@@ -309,7 +309,7 @@ def fieldsTest(format, self):
309 309
     management.call_command('flush', verbosity=0, interactive=False)
310 310
 
311 311
     obj = ComplexModel(field1='first',field2='second',field3='third')
312  
-    obj.save(raw=True)
  312
+    obj.save()
313 313
 
314 314
     # Serialize then deserialize the test database
315 315
     serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
@@ -325,7 +325,7 @@ def streamTest(format, self):
325 325
     management.call_command('flush', verbosity=0, interactive=False)
326 326
 
327 327
     obj = ComplexModel(field1='first',field2='second',field3='third')
328  
-    obj.save(raw=True)
  328
+    obj.save()
329 329
 
330 330
     # Serialize the test database to a stream
331 331
     stream = StringIO()

0 notes on commit 3176beb

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