Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #21174 -- transaction control in related manager methods

  • Loading branch information...
commit 1df3c49a1a1c11198d181ddd0ce31bbb42e631d8 1 parent 93cc6dc
Anssi Kääriäinen akaariai authored
6 django/contrib/contenttypes/generic.py
View
@@ -372,14 +372,12 @@ def add(self, *objs):
def remove(self, *objs):
db = router.db_for_write(self.model, instance=self.instance)
- for obj in objs:
- obj.delete(using=db)
+ self.using(db).filter(pk__in=[o.pk for o in objs]).delete()
remove.alters_data = True
def clear(self):
db = router.db_for_write(self.model, instance=self.instance)
- for obj in self.all():
- obj.delete(using=db)
+ self.using(db).delete()
clear.alters_data = True
def create(self, **kwargs):
18 django/db/models/fields/related.py
View
@@ -1,6 +1,6 @@
from operator import attrgetter
-from django.db import connection, connections, router
+from django.db import connection, connections, router, transaction
from django.db.backends import utils
from django.db.models import signals
from django.db.models.fields import (AutoField, Field, IntegerField,
@@ -18,7 +18,6 @@
RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
-
def add_lazy_relation(cls, field, relation, operation):
"""
Adds a lookup on ``cls`` when a related field is defined using a string,
@@ -416,11 +415,16 @@ def get_prefetch_queryset(self, instances):
return qs, rel_obj_attr, instance_attr, False, cache_name
def add(self, *objs):
- for obj in objs:
- if not isinstance(obj, self.model):
- raise TypeError("'%s' instance expected, got %r" % (self.model._meta.object_name, obj))
- setattr(obj, rel_field.name, self.instance)
- obj.save()
+ objs = list(objs)
+ db = router.db_for_write(self.model, instance=self.instance)
+ with transaction.commit_on_success_unless_managed(
+ using=db, savepoint=False):
+ for obj in objs:
+ if not isinstance(obj, self.model):
+ raise TypeError("'%s' instance expected, got %r" %
+ (self.model._meta.object_name, obj))
+ setattr(obj, rel_field.name, self.instance)
+ obj.save()
add.alters_data = True
def create(self, **kwargs):
7 tests/many_to_one/tests.py
View
@@ -2,6 +2,7 @@
import datetime
from django.core.exceptions import MultipleObjectsReturned, FieldError
+from django.db import transaction
from django.test import TestCase
from django.utils import six
from django.utils.translation import ugettext_lazy
@@ -68,8 +69,10 @@ def test_add(self):
self.assertQuerysetEqual(self.r2.article_set.all(), ["<Article: Paul's story>"])
# Adding an object of the wrong type raises TypeError.
- with six.assertRaisesRegex(self, TypeError, "'Article' instance expected, got <Reporter.*"):
- self.r.article_set.add(self.r2)
+ with transaction.atomic():
+ with six.assertRaisesRegex(self, TypeError,
+ "'Article' instance expected, got <Reporter.*"):
+ self.r.article_set.add(self.r2)
self.assertQuerysetEqual(self.r.article_set.all(),
[
"<Article: John's second story>",
11 tests/multiple_database/tests.py
View
@@ -7,7 +7,7 @@
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core import management
-from django.db import connections, router, DEFAULT_DB_ALIAS
+from django.db import connections, router, DEFAULT_DB_ALIAS, transaction
from django.db.models import signals
from django.db.utils import ConnectionRouter
from django.test import TestCase
@@ -490,21 +490,24 @@ def test_foreign_key_cross_database_protection(self):
# Set a foreign key with an object from a different database
try:
- dive.editor = marty
+ with transaction.atomic(using='default'):
+ dive.editor = marty
self.fail("Shouldn't be able to assign across databases")
except ValueError:
pass
# Set a foreign key set with an object from a different database
try:
- marty.edited = [pro, dive]
+ with transaction.atomic(using='default'):
+ marty.edited = [pro, dive]
self.fail("Shouldn't be able to assign across databases")
except ValueError:
pass
# Add to a foreign key set with an object from a different database
try:
- marty.edited.add(dive)
+ with transaction.atomic(using='default'):
+ marty.edited.add(dive)
self.fail("Shouldn't be able to assign across databases")
except ValueError:
pass
Please sign in to comment.
Something went wrong with that request. Please try again.