mirror of https://github.com/django/django.git
Used commit_on_success_unless_managed to make ORM operations atomic.
This commit is contained in:
parent
86fd920f67
commit
4dbd1b2dd8
|
@ -50,24 +50,6 @@ def DO_NOTHING(collector, field, sub_objs, using):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def force_managed(func):
|
|
||||||
@wraps(func)
|
|
||||||
def decorated(self, *args, **kwargs):
|
|
||||||
if transaction.get_autocommit(using=self.using):
|
|
||||||
transaction.enter_transaction_management(using=self.using, forced=True)
|
|
||||||
forced_managed = True
|
|
||||||
else:
|
|
||||||
forced_managed = False
|
|
||||||
try:
|
|
||||||
func(self, *args, **kwargs)
|
|
||||||
if forced_managed:
|
|
||||||
transaction.commit(using=self.using)
|
|
||||||
finally:
|
|
||||||
if forced_managed:
|
|
||||||
transaction.leave_transaction_management(using=self.using)
|
|
||||||
return decorated
|
|
||||||
|
|
||||||
|
|
||||||
class Collector(object):
|
class Collector(object):
|
||||||
def __init__(self, using):
|
def __init__(self, using):
|
||||||
self.using = using
|
self.using = using
|
||||||
|
@ -260,7 +242,6 @@ class Collector(object):
|
||||||
self.data = SortedDict([(model, self.data[model])
|
self.data = SortedDict([(model, self.data[model])
|
||||||
for model in sorted_models])
|
for model in sorted_models])
|
||||||
|
|
||||||
@force_managed
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
# sort instance collections
|
# sort instance collections
|
||||||
for model, instances in self.data.items():
|
for model, instances in self.data.items():
|
||||||
|
@ -271,40 +252,41 @@ class Collector(object):
|
||||||
# end of a transaction.
|
# end of a transaction.
|
||||||
self.sort()
|
self.sort()
|
||||||
|
|
||||||
# send pre_delete signals
|
with transaction.commit_on_success_unless_managed(using=self.using):
|
||||||
for model, obj in self.instances_with_model():
|
# send pre_delete signals
|
||||||
if not model._meta.auto_created:
|
for model, obj in self.instances_with_model():
|
||||||
signals.pre_delete.send(
|
if not model._meta.auto_created:
|
||||||
sender=model, instance=obj, using=self.using
|
signals.pre_delete.send(
|
||||||
)
|
|
||||||
|
|
||||||
# fast deletes
|
|
||||||
for qs in self.fast_deletes:
|
|
||||||
qs._raw_delete(using=self.using)
|
|
||||||
|
|
||||||
# update fields
|
|
||||||
for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
|
|
||||||
query = sql.UpdateQuery(model)
|
|
||||||
for (field, value), instances in six.iteritems(instances_for_fieldvalues):
|
|
||||||
query.update_batch([obj.pk for obj in instances],
|
|
||||||
{field.name: value}, self.using)
|
|
||||||
|
|
||||||
# reverse instance collections
|
|
||||||
for instances in six.itervalues(self.data):
|
|
||||||
instances.reverse()
|
|
||||||
|
|
||||||
# delete instances
|
|
||||||
for model, instances in six.iteritems(self.data):
|
|
||||||
query = sql.DeleteQuery(model)
|
|
||||||
pk_list = [obj.pk for obj in instances]
|
|
||||||
query.delete_batch(pk_list, self.using)
|
|
||||||
|
|
||||||
if not model._meta.auto_created:
|
|
||||||
for obj in instances:
|
|
||||||
signals.post_delete.send(
|
|
||||||
sender=model, instance=obj, using=self.using
|
sender=model, instance=obj, using=self.using
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# fast deletes
|
||||||
|
for qs in self.fast_deletes:
|
||||||
|
qs._raw_delete(using=self.using)
|
||||||
|
|
||||||
|
# update fields
|
||||||
|
for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
|
||||||
|
query = sql.UpdateQuery(model)
|
||||||
|
for (field, value), instances in six.iteritems(instances_for_fieldvalues):
|
||||||
|
query.update_batch([obj.pk for obj in instances],
|
||||||
|
{field.name: value}, self.using)
|
||||||
|
|
||||||
|
# reverse instance collections
|
||||||
|
for instances in six.itervalues(self.data):
|
||||||
|
instances.reverse()
|
||||||
|
|
||||||
|
# delete instances
|
||||||
|
for model, instances in six.iteritems(self.data):
|
||||||
|
query = sql.DeleteQuery(model)
|
||||||
|
pk_list = [obj.pk for obj in instances]
|
||||||
|
query.delete_batch(pk_list, self.using)
|
||||||
|
|
||||||
|
if not model._meta.auto_created:
|
||||||
|
for obj in instances:
|
||||||
|
signals.post_delete.send(
|
||||||
|
sender=model, instance=obj, using=self.using
|
||||||
|
)
|
||||||
|
|
||||||
# update collected instances
|
# update collected instances
|
||||||
for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
|
for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
|
||||||
for (field, value), instances in six.iteritems(instances_for_fieldvalues):
|
for (field, value), instances in six.iteritems(instances_for_fieldvalues):
|
||||||
|
|
|
@ -442,12 +442,7 @@ class QuerySet(object):
|
||||||
self._for_write = True
|
self._for_write = True
|
||||||
connection = connections[self.db]
|
connection = connections[self.db]
|
||||||
fields = self.model._meta.local_fields
|
fields = self.model._meta.local_fields
|
||||||
if transaction.get_autocommit(using=self.db):
|
with transaction.commit_on_success_unless_managed(using=self.db):
|
||||||
transaction.enter_transaction_management(using=self.db, forced=True)
|
|
||||||
forced_managed = True
|
|
||||||
else:
|
|
||||||
forced_managed = False
|
|
||||||
try:
|
|
||||||
if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk
|
if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk
|
||||||
and self.model._meta.has_auto_field):
|
and self.model._meta.has_auto_field):
|
||||||
self._batched_insert(objs, fields, batch_size)
|
self._batched_insert(objs, fields, batch_size)
|
||||||
|
@ -458,11 +453,6 @@ class QuerySet(object):
|
||||||
if objs_without_pk:
|
if objs_without_pk:
|
||||||
fields= [f for f in fields if not isinstance(f, AutoField)]
|
fields= [f for f in fields if not isinstance(f, AutoField)]
|
||||||
self._batched_insert(objs_without_pk, fields, batch_size)
|
self._batched_insert(objs_without_pk, fields, batch_size)
|
||||||
if forced_managed:
|
|
||||||
transaction.commit(using=self.db)
|
|
||||||
finally:
|
|
||||||
if forced_managed:
|
|
||||||
transaction.leave_transaction_management(using=self.db)
|
|
||||||
|
|
||||||
return objs
|
return objs
|
||||||
|
|
||||||
|
@ -579,18 +569,8 @@ class QuerySet(object):
|
||||||
self._for_write = True
|
self._for_write = True
|
||||||
query = self.query.clone(sql.UpdateQuery)
|
query = self.query.clone(sql.UpdateQuery)
|
||||||
query.add_update_values(kwargs)
|
query.add_update_values(kwargs)
|
||||||
if transaction.get_autocommit(using=self.db):
|
with transaction.commit_on_success_unless_managed(using=self.db):
|
||||||
transaction.enter_transaction_management(using=self.db, forced=True)
|
|
||||||
forced_managed = True
|
|
||||||
else:
|
|
||||||
forced_managed = False
|
|
||||||
try:
|
|
||||||
rows = query.get_compiler(self.db).execute_sql(None)
|
rows = query.get_compiler(self.db).execute_sql(None)
|
||||||
if forced_managed:
|
|
||||||
transaction.commit(using=self.db)
|
|
||||||
finally:
|
|
||||||
if forced_managed:
|
|
||||||
transaction.leave_transaction_management(using=self.db)
|
|
||||||
self._result_cache = None
|
self._result_cache = None
|
||||||
return rows
|
return rows
|
||||||
update.alters_data = True
|
update.alters_data = True
|
||||||
|
|
|
@ -16,11 +16,10 @@ Django's default behavior is to run in autocommit mode. Each query is
|
||||||
immediately committed to the database. :ref:`See below for details
|
immediately committed to the database. :ref:`See below for details
|
||||||
<autocommit-details>`.
|
<autocommit-details>`.
|
||||||
|
|
||||||
..
|
Django uses transactions or savepoints automatically to guarantee the
|
||||||
Django uses transactions or savepoints automatically to guarantee the
|
integrity of ORM operations that require multiple queries, especially
|
||||||
integrity of ORM operations that require multiple queries, especially
|
:ref:`delete() <topics-db-queries-delete>` and :ref:`update()
|
||||||
:ref:`delete() <topics-db-queries-delete>` and :ref:`update()
|
<topics-db-queries-update>` queries.
|
||||||
<topics-db-queries-update>` queries.
|
|
||||||
|
|
||||||
.. versionchanged:: 1.6
|
.. versionchanged:: 1.6
|
||||||
Previous version of Django featured :ref:`a more complicated default
|
Previous version of Django featured :ref:`a more complicated default
|
||||||
|
|
Loading…
Reference in New Issue