Loading django/db/models/base.py +41 −14 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ from django.core import validators from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields.related import (ManyToOneRel, OneToOneField, add_lazy_relation) from django.db import (connections, router, transaction, DatabaseError, from django.db import (router, transaction, DatabaseError, DEFAULT_DB_ALIAS) from django.db.models.query import Q from django.db.models.query_utils import DeferredAttribute Loading Loading @@ -449,7 +449,8 @@ class Model(object): return getattr(self, field_name) return getattr(self, field.attname) def save(self, force_insert=False, force_update=False, using=None): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): """ Saves the current instance. Override this in a subclass if you want to control the saving process. Loading @@ -458,14 +459,32 @@ class Model(object): that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ if force_insert and force_update: if force_insert and (force_update or update_fields): raise ValueError("Cannot force both insert and updating in model saving.") self.save_base(using=using, force_insert=force_insert, force_update=force_update) if update_fields is not None: # If update_fields is empty, skip the save. We do also check for # no-op saves later on for inheritance cases. This bailout is # still needed for skipping signal sending. if len(update_fields) == 0: return update_fields = frozenset(update_fields) field_names = set([field.name for field in self._meta.fields if not field.primary_key]) non_model_fields = update_fields.difference(field_names) if non_model_fields: raise ValueError("The following fields do not exist in this " "model or are m2m fields: %s" % ', '.join(non_model_fields)) self.save_base(using=using, force_insert=force_insert, force_update=force_update, update_fields=update_fields) save.alters_data = True def save_base(self, raw=False, cls=None, origin=None, force_insert=False, force_update=False, using=None): force_update=False, using=None, update_fields=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 Loading @@ -473,7 +492,8 @@ class Model(object): ('raw', 'cls', and 'origin'). """ using = using or router.db_for_write(self.__class__, instance=self) assert not (force_insert and force_update) assert not (force_insert and (force_update or update_fields)) assert update_fields is None or len(update_fields) > 0 if cls is None: cls = self.__class__ meta = cls._meta Loading @@ -483,7 +503,8 @@ class Model(object): meta = cls._meta if origin and not meta.auto_created: signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using) signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using, update_fields=update_fields) # If we are in a raw save, save the object exactly as presented. # That means that we don't try to be smart about saving attributes Loading @@ -503,7 +524,8 @@ class Model(object): if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) self.save_base(cls=parent, origin=org, using=using) self.save_base(cls=parent, origin=org, using=using, update_fields=update_fields) if field: setattr(self, field.attname, self._get_pk_val(parent._meta)) Loading @@ -513,22 +535,27 @@ class Model(object): if not meta.proxy: non_pks = [f for f in meta.local_fields if not f.primary_key] if update_fields: non_pks = [f for f in non_pks if f.name in update_fields] # First, try an UPDATE. If that doesn't update anything, do an INSERT. pk_val = self._get_pk_val(meta) pk_set = pk_val is not None record_exists = True manager = cls._base_manager if pk_set: # Determine whether a record with the primary key already exists. if (force_update or (not force_insert and # Determine if we should do an update (pk already exists, forced update, # no force_insert) if ((force_update or update_fields) or (not force_insert and manager.using(using).filter(pk=pk_val).exists())): # It does already exist, so do an UPDATE. if force_update or non_pks: values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] if values: rows = manager.using(using).filter(pk=pk_val)._update(values) if force_update and not rows: raise DatabaseError("Forced update did not affect any rows.") if update_fields and not rows: raise DatabaseError("Save with update_fields did not affect any rows.") else: record_exists = False if not pk_set or not record_exists: Loading @@ -541,7 +568,7 @@ class Model(object): fields = meta.local_fields if not pk_set: if force_update: if force_update or update_fields: raise ValueError("Cannot force an update in save() with no primary key.") fields = [f for f in fields if not isinstance(f, AutoField)] Loading @@ -561,8 +588,8 @@ class Model(object): # Signal that the save is complete if origin and not meta.auto_created: signals.post_save.send(sender=origin, instance=self, created=(not record_exists), raw=raw, using=using) signals.post_save.send(sender=origin, instance=self, created=(not record_exists), update_fields=update_fields, raw=raw, using=using) save_base.alters_data = True Loading django/db/models/signals.py +2 −2 Original line number Diff line number Diff line Loading @@ -5,8 +5,8 @@ class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"]) post_init = Signal(providing_args=["instance"]) pre_save = Signal(providing_args=["instance", "raw", "using"]) post_save = Signal(providing_args=["instance", "raw", "created", "using"]) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"]) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"]) pre_delete = Signal(providing_args=["instance", "using"]) post_delete = Signal(providing_args=["instance", "using"]) Loading docs/ref/models/instances.txt +23 −1 Original line number Diff line number Diff line Loading @@ -135,7 +135,7 @@ Saving objects To save an object back to the database, call ``save()``: .. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS]) .. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None]) .. versionadded:: 1.2 The ``using`` argument was added. Loading Loading @@ -289,6 +289,8 @@ almost always do the right thing and trying to override that will lead to errors that are difficult to track down. This feature is for advanced use only. Using ``update_fields`` will force an update similarly to ``force_update``. Updating attributes based on existing fields -------------------------------------------- Loading Loading @@ -334,6 +336,26 @@ For more details, see the documentation on :ref:`F() expressions <query-expressions>` and their :ref:`use in update queries <topics-db-queries-update>`. Specifying which fields to save ------------------------------- .. versionadded:: 1.5 If ``save()`` is passed a list of field names in keyword argument ``update_fields``, only the fields named in that list will be updated. This may be desirable if you want to update just one or a few fields on an object. There will be a slight performance benefit from preventing all of the model fields from being updated in the database. For example: product.name = 'Name changed again' product.save(update_fields=['name']) The ``update_fields`` argument can be any iterable containing strings. An empty ``update_fields`` iterable will skip the save. A value of None will perform an update on all fields. Specifying ``update_fields`` will force an update. Deleting objects ================ Loading docs/ref/signals.txt +12 −0 Original line number Diff line number Diff line Loading @@ -123,6 +123,12 @@ Arguments sent with this signal: ``using`` The database alias being used. .. versionadded:: 1.5 ``update_fields`` The set of fields to update explicitly specified in the ``save()`` method. ``None`` if this argument was not used in the ``save()`` call. post_save --------- Loading Loading @@ -154,6 +160,12 @@ Arguments sent with this signal: ``using`` The database alias being used. .. versionadded:: 1.5 ``update_fields`` The set of fields to update explicitly specified in the ``save()`` method. ``None`` if this argument was not used in the ``save()`` call. pre_delete ---------- Loading docs/releases/1.5.txt +11 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,17 @@ version compatible with Python 2.6. What's new in Django 1.5 ======================== Support for saving a subset of model's fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The method :meth:`Model.save() <django.db.models.Model.save()>` has a new keyword argument ``update_fields``. By using this argument it is possible to save only a select list of model's fields. This can be useful for performance reasons or when trying to avoid overwriting concurrent changes. See the :meth:`Model.save() <django.db.models.Model.save()>` documentation for more details. Minor features ~~~~~~~~~~~~~~ Loading Loading
django/db/models/base.py +41 −14 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ from django.core import validators from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields.related import (ManyToOneRel, OneToOneField, add_lazy_relation) from django.db import (connections, router, transaction, DatabaseError, from django.db import (router, transaction, DatabaseError, DEFAULT_DB_ALIAS) from django.db.models.query import Q from django.db.models.query_utils import DeferredAttribute Loading Loading @@ -449,7 +449,8 @@ class Model(object): return getattr(self, field_name) return getattr(self, field.attname) def save(self, force_insert=False, force_update=False, using=None): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): """ Saves the current instance. Override this in a subclass if you want to control the saving process. Loading @@ -458,14 +459,32 @@ class Model(object): that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ if force_insert and force_update: if force_insert and (force_update or update_fields): raise ValueError("Cannot force both insert and updating in model saving.") self.save_base(using=using, force_insert=force_insert, force_update=force_update) if update_fields is not None: # If update_fields is empty, skip the save. We do also check for # no-op saves later on for inheritance cases. This bailout is # still needed for skipping signal sending. if len(update_fields) == 0: return update_fields = frozenset(update_fields) field_names = set([field.name for field in self._meta.fields if not field.primary_key]) non_model_fields = update_fields.difference(field_names) if non_model_fields: raise ValueError("The following fields do not exist in this " "model or are m2m fields: %s" % ', '.join(non_model_fields)) self.save_base(using=using, force_insert=force_insert, force_update=force_update, update_fields=update_fields) save.alters_data = True def save_base(self, raw=False, cls=None, origin=None, force_insert=False, force_update=False, using=None): force_update=False, using=None, update_fields=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 Loading @@ -473,7 +492,8 @@ class Model(object): ('raw', 'cls', and 'origin'). """ using = using or router.db_for_write(self.__class__, instance=self) assert not (force_insert and force_update) assert not (force_insert and (force_update or update_fields)) assert update_fields is None or len(update_fields) > 0 if cls is None: cls = self.__class__ meta = cls._meta Loading @@ -483,7 +503,8 @@ class Model(object): meta = cls._meta if origin and not meta.auto_created: signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using) signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using, update_fields=update_fields) # If we are in a raw save, save the object exactly as presented. # That means that we don't try to be smart about saving attributes Loading @@ -503,7 +524,8 @@ class Model(object): if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None: setattr(self, parent._meta.pk.attname, getattr(self, field.attname)) self.save_base(cls=parent, origin=org, using=using) self.save_base(cls=parent, origin=org, using=using, update_fields=update_fields) if field: setattr(self, field.attname, self._get_pk_val(parent._meta)) Loading @@ -513,22 +535,27 @@ class Model(object): if not meta.proxy: non_pks = [f for f in meta.local_fields if not f.primary_key] if update_fields: non_pks = [f for f in non_pks if f.name in update_fields] # First, try an UPDATE. If that doesn't update anything, do an INSERT. pk_val = self._get_pk_val(meta) pk_set = pk_val is not None record_exists = True manager = cls._base_manager if pk_set: # Determine whether a record with the primary key already exists. if (force_update or (not force_insert and # Determine if we should do an update (pk already exists, forced update, # no force_insert) if ((force_update or update_fields) or (not force_insert and manager.using(using).filter(pk=pk_val).exists())): # It does already exist, so do an UPDATE. if force_update or non_pks: values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] if values: rows = manager.using(using).filter(pk=pk_val)._update(values) if force_update and not rows: raise DatabaseError("Forced update did not affect any rows.") if update_fields and not rows: raise DatabaseError("Save with update_fields did not affect any rows.") else: record_exists = False if not pk_set or not record_exists: Loading @@ -541,7 +568,7 @@ class Model(object): fields = meta.local_fields if not pk_set: if force_update: if force_update or update_fields: raise ValueError("Cannot force an update in save() with no primary key.") fields = [f for f in fields if not isinstance(f, AutoField)] Loading @@ -561,8 +588,8 @@ class Model(object): # Signal that the save is complete if origin and not meta.auto_created: signals.post_save.send(sender=origin, instance=self, created=(not record_exists), raw=raw, using=using) signals.post_save.send(sender=origin, instance=self, created=(not record_exists), update_fields=update_fields, raw=raw, using=using) save_base.alters_data = True Loading
django/db/models/signals.py +2 −2 Original line number Diff line number Diff line Loading @@ -5,8 +5,8 @@ class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"]) post_init = Signal(providing_args=["instance"]) pre_save = Signal(providing_args=["instance", "raw", "using"]) post_save = Signal(providing_args=["instance", "raw", "created", "using"]) pre_save = Signal(providing_args=["instance", "raw", "using", "update_fields"]) post_save = Signal(providing_args=["instance", "raw", "created", "using", "update_fields"]) pre_delete = Signal(providing_args=["instance", "using"]) post_delete = Signal(providing_args=["instance", "using"]) Loading
docs/ref/models/instances.txt +23 −1 Original line number Diff line number Diff line Loading @@ -135,7 +135,7 @@ Saving objects To save an object back to the database, call ``save()``: .. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS]) .. method:: Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None]) .. versionadded:: 1.2 The ``using`` argument was added. Loading Loading @@ -289,6 +289,8 @@ almost always do the right thing and trying to override that will lead to errors that are difficult to track down. This feature is for advanced use only. Using ``update_fields`` will force an update similarly to ``force_update``. Updating attributes based on existing fields -------------------------------------------- Loading Loading @@ -334,6 +336,26 @@ For more details, see the documentation on :ref:`F() expressions <query-expressions>` and their :ref:`use in update queries <topics-db-queries-update>`. Specifying which fields to save ------------------------------- .. versionadded:: 1.5 If ``save()`` is passed a list of field names in keyword argument ``update_fields``, only the fields named in that list will be updated. This may be desirable if you want to update just one or a few fields on an object. There will be a slight performance benefit from preventing all of the model fields from being updated in the database. For example: product.name = 'Name changed again' product.save(update_fields=['name']) The ``update_fields`` argument can be any iterable containing strings. An empty ``update_fields`` iterable will skip the save. A value of None will perform an update on all fields. Specifying ``update_fields`` will force an update. Deleting objects ================ Loading
docs/ref/signals.txt +12 −0 Original line number Diff line number Diff line Loading @@ -123,6 +123,12 @@ Arguments sent with this signal: ``using`` The database alias being used. .. versionadded:: 1.5 ``update_fields`` The set of fields to update explicitly specified in the ``save()`` method. ``None`` if this argument was not used in the ``save()`` call. post_save --------- Loading Loading @@ -154,6 +160,12 @@ Arguments sent with this signal: ``using`` The database alias being used. .. versionadded:: 1.5 ``update_fields`` The set of fields to update explicitly specified in the ``save()`` method. ``None`` if this argument was not used in the ``save()`` call. pre_delete ---------- Loading
docs/releases/1.5.txt +11 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,17 @@ version compatible with Python 2.6. What's new in Django 1.5 ======================== Support for saving a subset of model's fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The method :meth:`Model.save() <django.db.models.Model.save()>` has a new keyword argument ``update_fields``. By using this argument it is possible to save only a select list of model's fields. This can be useful for performance reasons or when trying to avoid overwriting concurrent changes. See the :meth:`Model.save() <django.db.models.Model.save()>` documentation for more details. Minor features ~~~~~~~~~~~~~~ Loading