Loading django/db/models/base.py +21 −7 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ from django.db.models.fields import AutoField from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField from django.db.models.query import delete_objects, Q, CollectedObjects from django.db.models.options import Options from django.db import connection, transaction from django.db import connection, transaction, DatabaseError from django.db.models import signals from django.db.models.loading import register_models, get_model from django.utils.functional import curry Loading Loading @@ -268,22 +268,31 @@ class Model(object): pk = property(_get_pk_val, _set_pk_val) def save(self): def save(self, force_insert=False, force_update=False): """ Saves the current instance. Override this in a subclass if you want to control the saving process. The 'force_insert' and 'force_update' parameters can be used to insist that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ self.save_base() if force_insert and force_update: raise ValueError("Cannot force both insert and updating in " "model saving.") self.save_base(force_insert=force_insert, force_update=force_update) save.alters_data = True def save_base(self, raw=False, cls=None): def save_base(self, raw=False, cls=None, force_insert=False, force_update=False): """ 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 need for overrides of save() to pass around internal-only parameters ('raw' and 'cls'). """ assert not (force_insert and force_update) if not cls: cls = self.__class__ meta = self._meta Loading Loading @@ -319,15 +328,20 @@ class Model(object): manager = cls._default_manager if pk_set: # Determine whether a record with the primary key already exists. if manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by(): if (force_update or (not force_insert and manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by())): # It does already exist, so do an UPDATE. if non_pks: if force_update or non_pks: values = [(f, None, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] manager.filter(pk=pk_val)._update(values) rows = manager.filter(pk=pk_val)._update(values) if force_update and not rows: raise DatabaseError("Forced update did not affect any rows.") else: record_exists = False if not pk_set or not record_exists: if not pk_set: if force_update: raise ValueError("Cannot force an update in save() with no primary key.") values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)] else: values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields] Loading django/db/models/query.py +3 −2 Original line number Diff line number Diff line Loading @@ -399,9 +399,10 @@ class QuerySet(object): "Cannot update a query once a slice has been taken." query = self.query.clone(sql.UpdateQuery) query.add_update_values(kwargs) query.execute_sql(None) rows = query.execute_sql(None) transaction.commit_unless_managed() self._result_cache = None return rows update.alters_data = True def _update(self, values): Loading @@ -415,8 +416,8 @@ class QuerySet(object): "Cannot update a query once a slice has been taken." query = self.query.clone(sql.UpdateQuery) query.add_update_fields(values) query.execute_sql(None) self._result_cache = None return query.execute_sql(None) _update.alters_data = True ################################################## Loading django/db/models/sql/subqueries.py +9 −1 Original line number Diff line number Diff line Loading @@ -109,9 +109,17 @@ class UpdateQuery(Query): related_updates=self.related_updates.copy, **kwargs) def execute_sql(self, result_type=None): super(UpdateQuery, self).execute_sql(result_type) """ Execute the specified update. Returns the number of rows affected by the primary update query (there could be other updates on related tables, but their rowcounts are not returned). """ cursor = super(UpdateQuery, self).execute_sql(result_type) rows = cursor.rowcount del cursor for query in self.get_related_updates(): query.execute_sql(result_type) return rows def as_sql(self): """ Loading docs/db-api.txt +20 −2 Original line number Diff line number Diff line Loading @@ -213,8 +213,26 @@ follows this algorithm: The one gotcha here is that you should be careful not to specify a primary-key value explicitly when saving new objects, if you cannot guarantee the primary-key value is unused. For more on this nuance, see "Explicitly specifying auto-primary-key values" above. primary-key value is unused. For more on this nuance, see `Explicitly specifying auto-primary-key values`_ above and `Forcing an INSERT or UPDATE`_ below. Forcing an INSERT or UPDATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~ **New in Django development version** In some rare circumstances, it's necesary to be able to force the ``save()`` method to perform an SQL ``INSERT`` and not fall back to doing an ``UPDATE``. Or vice-versa: update, if possible, but not insert a new row. In these cases you can pass the ``force_insert=True`` or ``force_update=True`` parameters to the ``save()`` method. Passing both parameters is an error, since you cannot both insert *and* update at the same time. It should be very rare that you'll need to use these parameters. Django will 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. Retrieving objects ================== Loading tests/modeltests/force_insert_update/__init__.py 0 → 100644 +0 −0 Empty file added. Loading
django/db/models/base.py +21 −7 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ from django.db.models.fields import AutoField from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField from django.db.models.query import delete_objects, Q, CollectedObjects from django.db.models.options import Options from django.db import connection, transaction from django.db import connection, transaction, DatabaseError from django.db.models import signals from django.db.models.loading import register_models, get_model from django.utils.functional import curry Loading Loading @@ -268,22 +268,31 @@ class Model(object): pk = property(_get_pk_val, _set_pk_val) def save(self): def save(self, force_insert=False, force_update=False): """ Saves the current instance. Override this in a subclass if you want to control the saving process. The 'force_insert' and 'force_update' parameters can be used to insist that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ self.save_base() if force_insert and force_update: raise ValueError("Cannot force both insert and updating in " "model saving.") self.save_base(force_insert=force_insert, force_update=force_update) save.alters_data = True def save_base(self, raw=False, cls=None): def save_base(self, raw=False, cls=None, force_insert=False, force_update=False): """ 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 need for overrides of save() to pass around internal-only parameters ('raw' and 'cls'). """ assert not (force_insert and force_update) if not cls: cls = self.__class__ meta = self._meta Loading Loading @@ -319,15 +328,20 @@ class Model(object): manager = cls._default_manager if pk_set: # Determine whether a record with the primary key already exists. if manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by(): if (force_update or (not force_insert and manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by())): # It does already exist, so do an UPDATE. if non_pks: if force_update or non_pks: values = [(f, None, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks] manager.filter(pk=pk_val)._update(values) rows = manager.filter(pk=pk_val)._update(values) if force_update and not rows: raise DatabaseError("Forced update did not affect any rows.") else: record_exists = False if not pk_set or not record_exists: if not pk_set: if force_update: raise ValueError("Cannot force an update in save() with no primary key.") values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)] else: values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields] Loading
django/db/models/query.py +3 −2 Original line number Diff line number Diff line Loading @@ -399,9 +399,10 @@ class QuerySet(object): "Cannot update a query once a slice has been taken." query = self.query.clone(sql.UpdateQuery) query.add_update_values(kwargs) query.execute_sql(None) rows = query.execute_sql(None) transaction.commit_unless_managed() self._result_cache = None return rows update.alters_data = True def _update(self, values): Loading @@ -415,8 +416,8 @@ class QuerySet(object): "Cannot update a query once a slice has been taken." query = self.query.clone(sql.UpdateQuery) query.add_update_fields(values) query.execute_sql(None) self._result_cache = None return query.execute_sql(None) _update.alters_data = True ################################################## Loading
django/db/models/sql/subqueries.py +9 −1 Original line number Diff line number Diff line Loading @@ -109,9 +109,17 @@ class UpdateQuery(Query): related_updates=self.related_updates.copy, **kwargs) def execute_sql(self, result_type=None): super(UpdateQuery, self).execute_sql(result_type) """ Execute the specified update. Returns the number of rows affected by the primary update query (there could be other updates on related tables, but their rowcounts are not returned). """ cursor = super(UpdateQuery, self).execute_sql(result_type) rows = cursor.rowcount del cursor for query in self.get_related_updates(): query.execute_sql(result_type) return rows def as_sql(self): """ Loading
docs/db-api.txt +20 −2 Original line number Diff line number Diff line Loading @@ -213,8 +213,26 @@ follows this algorithm: The one gotcha here is that you should be careful not to specify a primary-key value explicitly when saving new objects, if you cannot guarantee the primary-key value is unused. For more on this nuance, see "Explicitly specifying auto-primary-key values" above. primary-key value is unused. For more on this nuance, see `Explicitly specifying auto-primary-key values`_ above and `Forcing an INSERT or UPDATE`_ below. Forcing an INSERT or UPDATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~ **New in Django development version** In some rare circumstances, it's necesary to be able to force the ``save()`` method to perform an SQL ``INSERT`` and not fall back to doing an ``UPDATE``. Or vice-versa: update, if possible, but not insert a new row. In these cases you can pass the ``force_insert=True`` or ``force_update=True`` parameters to the ``save()`` method. Passing both parameters is an error, since you cannot both insert *and* update at the same time. It should be very rare that you'll need to use these parameters. Django will 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. Retrieving objects ================== Loading