mirror of https://github.com/django/django.git
Fixed #32107 -- Fixed ProtectedError.protected_objects and RestrictedError.restricted_objects.
Regression in4ca5c565f4
andab3cbd8b9a
. Thanks Vitaliy Yelnik for the report.
This commit is contained in:
parent
0eee5c1b9c
commit
3b1746d519
|
@ -305,7 +305,7 @@ class Collector:
|
|||
model.__name__,
|
||||
', '.join(protected_objects),
|
||||
),
|
||||
chain.from_iterable(protected_objects.values()),
|
||||
set(chain.from_iterable(protected_objects.values())),
|
||||
)
|
||||
for related_model, related_fields in model_fast_deletes.items():
|
||||
batches = self.get_del_batches(new_objs, related_fields)
|
||||
|
@ -340,7 +340,7 @@ class Collector:
|
|||
model.__name__,
|
||||
', '.join(restricted_objects),
|
||||
),
|
||||
chain.from_iterable(restricted_objects.values()),
|
||||
set(chain.from_iterable(restricted_objects.values())),
|
||||
)
|
||||
|
||||
def related_objects(self, related_model, related_fields, objs):
|
||||
|
|
|
@ -39,3 +39,9 @@ Bugfixes
|
|||
:class:`~django.contrib.postgres.constraints.ExclusionConstraint` with key
|
||||
transforms for :class:`~django.db.models.JSONField` in ``expressions``
|
||||
(:ticket:`32096`).
|
||||
|
||||
* Fixed a regression in Django 3.1 where
|
||||
:exc:`ProtectedError.protected_objects <django.db.models.ProtectedError>` and
|
||||
:exc:`RestrictedError.restricted_objects <django.db.models.RestrictedError>`
|
||||
attributes returned iterators instead of :py:class:`set` of objects
|
||||
(:ticket:`32107`).
|
||||
|
|
|
@ -75,19 +75,21 @@ class OnDeleteTests(TestCase):
|
|||
"Cannot delete some instances of model 'R' because they are "
|
||||
"referenced through protected foreign keys: 'A.protect'."
|
||||
)
|
||||
with self.assertRaisesMessage(ProtectedError, msg):
|
||||
with self.assertRaisesMessage(ProtectedError, msg) as cm:
|
||||
a.protect.delete()
|
||||
self.assertEqual(cm.exception.protected_objects, {a})
|
||||
|
||||
def test_protect_multiple(self):
|
||||
a = create_a('protect')
|
||||
B.objects.create(protect=a.protect)
|
||||
b = B.objects.create(protect=a.protect)
|
||||
msg = (
|
||||
"Cannot delete some instances of model 'R' because they are "
|
||||
"referenced through protected foreign keys: 'A.protect', "
|
||||
"'B.protect'."
|
||||
)
|
||||
with self.assertRaisesMessage(ProtectedError, msg):
|
||||
with self.assertRaisesMessage(ProtectedError, msg) as cm:
|
||||
a.protect.delete()
|
||||
self.assertEqual(cm.exception.protected_objects, {a, b})
|
||||
|
||||
def test_protect_path(self):
|
||||
a = create_a('protect')
|
||||
|
@ -97,8 +99,9 @@ class OnDeleteTests(TestCase):
|
|||
"Cannot delete some instances of model 'P' because they are "
|
||||
"referenced through protected foreign keys: 'R.p'."
|
||||
)
|
||||
with self.assertRaisesMessage(ProtectedError, msg):
|
||||
with self.assertRaisesMessage(ProtectedError, msg) as cm:
|
||||
a.protect.p.delete()
|
||||
self.assertEqual(cm.exception.protected_objects, {a})
|
||||
|
||||
def test_do_nothing(self):
|
||||
# Testing DO_NOTHING is a bit harder: It would raise IntegrityError for a normal model,
|
||||
|
@ -176,19 +179,21 @@ class OnDeleteTests(TestCase):
|
|||
"Cannot delete some instances of model 'R' because they are "
|
||||
"referenced through restricted foreign keys: 'A.restrict'."
|
||||
)
|
||||
with self.assertRaisesMessage(RestrictedError, msg):
|
||||
with self.assertRaisesMessage(RestrictedError, msg) as cm:
|
||||
a.restrict.delete()
|
||||
self.assertEqual(cm.exception.restricted_objects, {a})
|
||||
|
||||
def test_restrict_multiple(self):
|
||||
a = create_a('restrict')
|
||||
B3.objects.create(restrict=a.restrict)
|
||||
b3 = B3.objects.create(restrict=a.restrict)
|
||||
msg = (
|
||||
"Cannot delete some instances of model 'R' because they are "
|
||||
"referenced through restricted foreign keys: 'A.restrict', "
|
||||
"'B3.restrict'."
|
||||
)
|
||||
with self.assertRaisesMessage(RestrictedError, msg):
|
||||
with self.assertRaisesMessage(RestrictedError, msg) as cm:
|
||||
a.restrict.delete()
|
||||
self.assertEqual(cm.exception.restricted_objects, {a, b3})
|
||||
|
||||
def test_restrict_path_cascade_indirect(self):
|
||||
a = create_a('restrict')
|
||||
|
@ -198,8 +203,9 @@ class OnDeleteTests(TestCase):
|
|||
"Cannot delete some instances of model 'P' because they are "
|
||||
"referenced through restricted foreign keys: 'A.restrict'."
|
||||
)
|
||||
with self.assertRaisesMessage(RestrictedError, msg):
|
||||
with self.assertRaisesMessage(RestrictedError, msg) as cm:
|
||||
a.restrict.p.delete()
|
||||
self.assertEqual(cm.exception.restricted_objects, {a})
|
||||
# Object referenced also with CASCADE relationship can be deleted.
|
||||
a.cascade.p = a.restrict.p
|
||||
a.cascade.save()
|
||||
|
@ -221,13 +227,14 @@ class OnDeleteTests(TestCase):
|
|||
delete_top = DeleteTop.objects.create()
|
||||
b1 = B1.objects.create(delete_top=delete_top)
|
||||
b2 = B2.objects.create(delete_top=delete_top)
|
||||
DeleteBottom.objects.create(b1=b1, b2=b2)
|
||||
delete_bottom = DeleteBottom.objects.create(b1=b1, b2=b2)
|
||||
msg = (
|
||||
"Cannot delete some instances of model 'B1' because they are "
|
||||
"referenced through restricted foreign keys: 'DeleteBottom.b1'."
|
||||
)
|
||||
with self.assertRaisesMessage(RestrictedError, msg):
|
||||
with self.assertRaisesMessage(RestrictedError, msg) as cm:
|
||||
b1.delete()
|
||||
self.assertEqual(cm.exception.restricted_objects, {delete_bottom})
|
||||
self.assertTrue(DeleteTop.objects.exists())
|
||||
self.assertTrue(B1.objects.exists())
|
||||
self.assertTrue(B2.objects.exists())
|
||||
|
@ -243,14 +250,18 @@ class OnDeleteTests(TestCase):
|
|||
delete_top = DeleteTop.objects.create()
|
||||
generic_b1 = GenericB1.objects.create(generic_delete_top=delete_top)
|
||||
generic_b2 = GenericB2.objects.create(generic_delete_top=delete_top)
|
||||
GenericDeleteBottom.objects.create(generic_b1=generic_b1, generic_b2=generic_b2)
|
||||
generic_delete_bottom = GenericDeleteBottom.objects.create(
|
||||
generic_b1=generic_b1,
|
||||
generic_b2=generic_b2,
|
||||
)
|
||||
msg = (
|
||||
"Cannot delete some instances of model 'GenericB1' because they "
|
||||
"are referenced through restricted foreign keys: "
|
||||
"'GenericDeleteBottom.generic_b1'."
|
||||
)
|
||||
with self.assertRaisesMessage(RestrictedError, msg):
|
||||
with self.assertRaisesMessage(RestrictedError, msg) as cm:
|
||||
generic_b1.delete()
|
||||
self.assertEqual(cm.exception.restricted_objects, {generic_delete_bottom})
|
||||
self.assertTrue(DeleteTop.objects.exists())
|
||||
self.assertTrue(GenericB1.objects.exists())
|
||||
self.assertTrue(GenericB2.objects.exists())
|
||||
|
|
Loading…
Reference in New Issue