summaryrefslogtreecommitdiff
path: root/parts/django/tests/regressiontests/delete_regress
diff options
context:
space:
mode:
Diffstat (limited to 'parts/django/tests/regressiontests/delete_regress')
-rw-r--r--parts/django/tests/regressiontests/delete_regress/__init__.py1
-rw-r--r--parts/django/tests/regressiontests/delete_regress/models.py37
-rw-r--r--parts/django/tests/regressiontests/delete_regress/tests.py116
3 files changed, 154 insertions, 0 deletions
diff --git a/parts/django/tests/regressiontests/delete_regress/__init__.py b/parts/django/tests/regressiontests/delete_regress/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/parts/django/tests/regressiontests/delete_regress/__init__.py
@@ -0,0 +1 @@
+
diff --git a/parts/django/tests/regressiontests/delete_regress/models.py b/parts/django/tests/regressiontests/delete_regress/models.py
new file mode 100644
index 0000000..8109c0a
--- /dev/null
+++ b/parts/django/tests/regressiontests/delete_regress/models.py
@@ -0,0 +1,37 @@
+from django.db import models
+
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+
+class Award(models.Model):
+ name = models.CharField(max_length=25)
+ object_id = models.PositiveIntegerField()
+ content_type = models.ForeignKey(ContentType)
+ content_object = generic.GenericForeignKey()
+
+class AwardNote(models.Model):
+ award = models.ForeignKey(Award)
+ note = models.CharField(max_length=100)
+
+class Person(models.Model):
+ name = models.CharField(max_length=25)
+ awards = generic.GenericRelation(Award)
+
+class Book(models.Model):
+ pagecount = models.IntegerField()
+
+class Toy(models.Model):
+ name = models.CharField(max_length=50)
+
+class Child(models.Model):
+ name = models.CharField(max_length=50)
+ toys = models.ManyToManyField(Toy, through='PlayedWith')
+
+class PlayedWith(models.Model):
+ child = models.ForeignKey(Child)
+ toy = models.ForeignKey(Toy)
+ date = models.DateField(db_column='date_col')
+
+class PlayedWithNote(models.Model):
+ played = models.ForeignKey(PlayedWith)
+ note = models.TextField()
diff --git a/parts/django/tests/regressiontests/delete_regress/tests.py b/parts/django/tests/regressiontests/delete_regress/tests.py
new file mode 100644
index 0000000..26cd3c5
--- /dev/null
+++ b/parts/django/tests/regressiontests/delete_regress/tests.py
@@ -0,0 +1,116 @@
+import datetime
+
+from django.conf import settings
+from django.db import backend, connection, transaction, DEFAULT_DB_ALIAS
+from django.test import TestCase, TransactionTestCase
+
+from models import Book, Award, AwardNote, Person, Child, Toy, PlayedWith, PlayedWithNote
+
+# Can't run this test under SQLite, because you can't
+# get two connections to an in-memory database.
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3':
+ class DeleteLockingTest(TransactionTestCase):
+ def setUp(self):
+ # Create a second connection to the default database
+ conn_settings = settings.DATABASES[DEFAULT_DB_ALIAS]
+ self.conn2 = backend.DatabaseWrapper({
+ 'HOST': conn_settings['HOST'],
+ 'NAME': conn_settings['NAME'],
+ 'OPTIONS': conn_settings['OPTIONS'],
+ 'PASSWORD': conn_settings['PASSWORD'],
+ 'PORT': conn_settings['PORT'],
+ 'USER': conn_settings['USER'],
+ 'TIME_ZONE': settings.TIME_ZONE,
+ })
+
+ # Put both DB connections into managed transaction mode
+ transaction.enter_transaction_management()
+ transaction.managed(True)
+ self.conn2._enter_transaction_management(True)
+
+ def tearDown(self):
+ # Close down the second connection.
+ transaction.leave_transaction_management()
+ self.conn2.close()
+
+ def test_concurrent_delete(self):
+ "Deletes on concurrent transactions don't collide and lock the database. Regression for #9479"
+
+ # Create some dummy data
+ b1 = Book(id=1, pagecount=100)
+ b2 = Book(id=2, pagecount=200)
+ b3 = Book(id=3, pagecount=300)
+ b1.save()
+ b2.save()
+ b3.save()
+
+ transaction.commit()
+
+ self.assertEquals(3, Book.objects.count())
+
+ # Delete something using connection 2.
+ cursor2 = self.conn2.cursor()
+ cursor2.execute('DELETE from delete_regress_book WHERE id=1')
+ self.conn2._commit();
+
+ # Now perform a queryset delete that covers the object
+ # deleted in connection 2. This causes an infinite loop
+ # under MySQL InnoDB unless we keep track of already
+ # deleted objects.
+ Book.objects.filter(pagecount__lt=250).delete()
+ transaction.commit()
+ self.assertEquals(1, Book.objects.count())
+
+class DeleteCascadeTests(TestCase):
+ def test_generic_relation_cascade(self):
+ """
+ Test that Django cascades deletes through generic-related
+ objects to their reverse relations.
+
+ This might falsely succeed if the database cascades deletes
+ itself immediately; the postgresql_psycopg2 backend does not
+ give such a false success because ForeignKeys are created with
+ DEFERRABLE INITIALLY DEFERRED, so its internal cascade is
+ delayed until transaction commit.
+
+ """
+ person = Person.objects.create(name='Nelson Mandela')
+ award = Award.objects.create(name='Nobel', content_object=person)
+ note = AwardNote.objects.create(note='a peace prize',
+ award=award)
+ self.assertEquals(AwardNote.objects.count(), 1)
+ person.delete()
+ self.assertEquals(Award.objects.count(), 0)
+ # first two asserts are just sanity checks, this is the kicker:
+ self.assertEquals(AwardNote.objects.count(), 0)
+
+ def test_fk_to_m2m_through(self):
+ """
+ Test that if a M2M relationship has an explicitly-specified
+ through model, and some other model has an FK to that through
+ model, deletion is cascaded from one of the participants in
+ the M2M, to the through model, to its related model.
+
+ Like the above test, this could in theory falsely succeed if
+ the DB cascades deletes itself immediately.
+
+ """
+ juan = Child.objects.create(name='Juan')
+ paints = Toy.objects.create(name='Paints')
+ played = PlayedWith.objects.create(child=juan, toy=paints,
+ date=datetime.date.today())
+ note = PlayedWithNote.objects.create(played=played,
+ note='the next Jackson Pollock')
+ self.assertEquals(PlayedWithNote.objects.count(), 1)
+ paints.delete()
+ self.assertEquals(PlayedWith.objects.count(), 0)
+ # first two asserts just sanity checks, this is the kicker:
+ self.assertEquals(PlayedWithNote.objects.count(), 0)
+
+class LargeDeleteTests(TestCase):
+ def test_large_deletes(self):
+ "Regression for #13309 -- if the number of objects > chunk size, deletion still occurs"
+ for x in range(300):
+ track = Book.objects.create(pagecount=x+100)
+ Book.objects.all().delete()
+ self.assertEquals(Book.objects.count(), 0)