Refs #27852 -- Fixed object deletion to show all restricted related objects rather than just the first one.
This commit is contained in:
parent
2a6fc89018
commit
4ca5c565f4
|
@ -328,18 +328,22 @@ class Collector:
|
||||||
self.clear_restricted_objects_from_set(related_model, instances)
|
self.clear_restricted_objects_from_set(related_model, instances)
|
||||||
for qs in self.fast_deletes:
|
for qs in self.fast_deletes:
|
||||||
self.clear_restricted_objects_from_queryset(qs.model, qs)
|
self.clear_restricted_objects_from_queryset(qs.model, qs)
|
||||||
for model, fields in self.restricted_objects.items():
|
if self.restricted_objects.values():
|
||||||
|
restricted_objects = defaultdict(list)
|
||||||
|
for related_model, fields in self.restricted_objects.items():
|
||||||
for field, objs in fields.items():
|
for field, objs in fields.items():
|
||||||
for obj in objs:
|
if objs:
|
||||||
|
key = "'%s.%s'" % (related_model.__name__, field.name)
|
||||||
|
restricted_objects[key] += objs
|
||||||
|
if restricted_objects:
|
||||||
raise RestrictedError(
|
raise RestrictedError(
|
||||||
"Cannot delete some instances of model '%s' "
|
'Cannot delete some instances of model %r because '
|
||||||
"because they are referenced through a restricted "
|
'they are referenced through restricted foreign keys: '
|
||||||
"foreign key: '%s.%s'." % (
|
'%s.' % (
|
||||||
field.remote_field.model.__name__,
|
model.__name__,
|
||||||
obj.__class__.__name__,
|
', '.join(restricted_objects),
|
||||||
field.name,
|
|
||||||
),
|
),
|
||||||
objs,
|
chain.from_iterable(restricted_objects.values()),
|
||||||
)
|
)
|
||||||
|
|
||||||
def related_objects(self, related_model, related_fields, objs):
|
def related_objects(self, related_model, related_fields, objs):
|
||||||
|
|
|
@ -177,6 +177,10 @@ class B2(models.Model):
|
||||||
delete_top = models.ForeignKey(DeleteTop, models.CASCADE)
|
delete_top = models.ForeignKey(DeleteTop, models.CASCADE)
|
||||||
|
|
||||||
|
|
||||||
|
class B3(models.Model):
|
||||||
|
restrict = models.ForeignKey(R, models.RESTRICT)
|
||||||
|
|
||||||
|
|
||||||
class DeleteBottom(models.Model):
|
class DeleteBottom(models.Model):
|
||||||
b1 = models.ForeignKey(B1, models.RESTRICT)
|
b1 = models.ForeignKey(B1, models.RESTRICT)
|
||||||
b2 = models.ForeignKey(B2, models.CASCADE)
|
b2 = models.ForeignKey(B2, models.CASCADE)
|
||||||
|
|
|
@ -8,10 +8,10 @@ from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE
|
||||||
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
B1, B2, MR, A, Avatar, B, Base, Child, DeleteBottom, DeleteTop, GenericB1,
|
B1, B2, B3, MR, A, Avatar, B, Base, Child, DeleteBottom, DeleteTop,
|
||||||
GenericB2, GenericDeleteBottom, HiddenUser, HiddenUserProfile, M, M2MFrom,
|
GenericB1, GenericB2, GenericDeleteBottom, HiddenUser, HiddenUserProfile,
|
||||||
M2MTo, MRNull, Origin, P, Parent, R, RChild, RChildChild, Referrer, S, T,
|
M, M2MFrom, M2MTo, MRNull, Origin, P, Parent, R, RChild, RChildChild,
|
||||||
User, create_a, get_default_r,
|
Referrer, S, T, User, create_a, get_default_r,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,7 +164,18 @@ class OnDeleteTests(TestCase):
|
||||||
a = create_a('restrict')
|
a = create_a('restrict')
|
||||||
msg = (
|
msg = (
|
||||||
"Cannot delete some instances of model 'R' because they are "
|
"Cannot delete some instances of model 'R' because they are "
|
||||||
"referenced through a restricted foreign key: 'A.restrict'."
|
"referenced through restricted foreign keys: 'A.restrict'."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(RestrictedError, msg):
|
||||||
|
a.restrict.delete()
|
||||||
|
|
||||||
|
def test_restrict_multiple(self):
|
||||||
|
a = create_a('restrict')
|
||||||
|
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):
|
||||||
a.restrict.delete()
|
a.restrict.delete()
|
||||||
|
@ -174,8 +185,8 @@ class OnDeleteTests(TestCase):
|
||||||
a.restrict.p = P.objects.create()
|
a.restrict.p = P.objects.create()
|
||||||
a.restrict.save()
|
a.restrict.save()
|
||||||
msg = (
|
msg = (
|
||||||
"Cannot delete some instances of model 'R' because they are "
|
"Cannot delete some instances of model 'P' because they are "
|
||||||
"referenced through a restricted foreign key: 'A.restrict'."
|
"referenced through restricted foreign keys: 'A.restrict'."
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(RestrictedError, msg):
|
with self.assertRaisesMessage(RestrictedError, msg):
|
||||||
a.restrict.p.delete()
|
a.restrict.p.delete()
|
||||||
|
@ -203,7 +214,7 @@ class OnDeleteTests(TestCase):
|
||||||
DeleteBottom.objects.create(b1=b1, b2=b2)
|
DeleteBottom.objects.create(b1=b1, b2=b2)
|
||||||
msg = (
|
msg = (
|
||||||
"Cannot delete some instances of model 'B1' because they are "
|
"Cannot delete some instances of model 'B1' because they are "
|
||||||
"referenced through a restricted foreign key: 'DeleteBottom.b1'."
|
"referenced through restricted foreign keys: 'DeleteBottom.b1'."
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(RestrictedError, msg):
|
with self.assertRaisesMessage(RestrictedError, msg):
|
||||||
b1.delete()
|
b1.delete()
|
||||||
|
@ -225,7 +236,7 @@ class OnDeleteTests(TestCase):
|
||||||
GenericDeleteBottom.objects.create(generic_b1=generic_b1, generic_b2=generic_b2)
|
GenericDeleteBottom.objects.create(generic_b1=generic_b1, generic_b2=generic_b2)
|
||||||
msg = (
|
msg = (
|
||||||
"Cannot delete some instances of model 'GenericB1' because they "
|
"Cannot delete some instances of model 'GenericB1' because they "
|
||||||
"are referenced through a restricted foreign key: "
|
"are referenced through restricted foreign keys: "
|
||||||
"'GenericDeleteBottom.generic_b1'."
|
"'GenericDeleteBottom.generic_b1'."
|
||||||
)
|
)
|
||||||
with self.assertRaisesMessage(RestrictedError, msg):
|
with self.assertRaisesMessage(RestrictedError, msg):
|
||||||
|
|
Loading…
Reference in New Issue