Fixed #31187 -- Fixed detecting of existing total ordering in admin changelist when using Meta.constraints.
Detection of existing total ordering in admin changelist now takes into account non-partial unique constraints.
This commit is contained in:
parent
d270c10a72
commit
b457068cf2
|
@ -367,8 +367,16 @@ class ChangeList:
|
||||||
break
|
break
|
||||||
ordering_fields.add(field.attname)
|
ordering_fields.add(field.attname)
|
||||||
else:
|
else:
|
||||||
# No single total ordering field, try unique_together.
|
# No single total ordering field, try unique_together and total
|
||||||
for field_names in self.lookup_opts.unique_together:
|
# unique constraints.
|
||||||
|
constraint_field_names = (
|
||||||
|
*self.lookup_opts.unique_together,
|
||||||
|
*(
|
||||||
|
constraint.fields
|
||||||
|
for constraint in self.lookup_opts.total_unique_constraints
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for field_names in constraint_field_names:
|
||||||
# Normalize attname references by using get_field().
|
# Normalize attname references by using get_field().
|
||||||
fields = [self.lookup_opts.get_field(field_name) for field_name in field_names]
|
fields = [self.lookup_opts.get_field(field_name) for field_name in field_names]
|
||||||
# Composite unique constraints containing a nullable column
|
# Composite unique constraints containing a nullable column
|
||||||
|
|
|
@ -1058,6 +1058,98 @@ class ChangeListTests(TestCase):
|
||||||
with self.subTest(ordering=ordering):
|
with self.subTest(ordering=ordering):
|
||||||
self.assertEqual(change_list._get_deterministic_ordering(ordering), expected)
|
self.assertEqual(change_list._get_deterministic_ordering(ordering), expected)
|
||||||
|
|
||||||
|
@isolate_apps('admin_changelist')
|
||||||
|
def test_total_ordering_optimization_meta_constraints(self):
|
||||||
|
class Related(models.Model):
|
||||||
|
unique_field = models.BooleanField(unique=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('unique_field',)
|
||||||
|
|
||||||
|
class Model(models.Model):
|
||||||
|
field_1 = models.BooleanField()
|
||||||
|
field_2 = models.BooleanField()
|
||||||
|
field_3 = models.BooleanField()
|
||||||
|
field_4 = models.BooleanField()
|
||||||
|
field_5 = models.BooleanField()
|
||||||
|
field_6 = models.BooleanField()
|
||||||
|
nullable_1 = models.BooleanField(null=True)
|
||||||
|
nullable_2 = models.BooleanField(null=True)
|
||||||
|
related_1 = models.ForeignKey(Related, models.CASCADE)
|
||||||
|
related_2 = models.ForeignKey(Related, models.CASCADE)
|
||||||
|
related_3 = models.ForeignKey(Related, models.CASCADE)
|
||||||
|
related_4 = models.ForeignKey(Related, models.CASCADE)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
*[
|
||||||
|
models.UniqueConstraint(fields=fields, name=''.join(fields))
|
||||||
|
for fields in (
|
||||||
|
['field_1'],
|
||||||
|
['nullable_1'],
|
||||||
|
['related_1'],
|
||||||
|
['related_2_id'],
|
||||||
|
['field_2', 'field_3'],
|
||||||
|
['field_2', 'nullable_2'],
|
||||||
|
['field_2', 'related_3'],
|
||||||
|
['field_3', 'related_4_id'],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
models.CheckConstraint(check=models.Q(id__gt=0), name='foo'),
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=['field_5'],
|
||||||
|
condition=models.Q(id__gt=10),
|
||||||
|
name='total_ordering_1',
|
||||||
|
),
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=['field_6'],
|
||||||
|
condition=models.Q(),
|
||||||
|
name='total_ordering',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
class ModelAdmin(admin.ModelAdmin):
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return Model.objects.none()
|
||||||
|
|
||||||
|
request = self._mocked_authenticated_request('/', self.superuser)
|
||||||
|
site = admin.AdminSite(name='admin')
|
||||||
|
model_admin = ModelAdmin(Model, site)
|
||||||
|
change_list = model_admin.get_changelist_instance(request)
|
||||||
|
tests = (
|
||||||
|
# Unique non-nullable field.
|
||||||
|
(['field_1'], ['field_1']),
|
||||||
|
# Unique nullable field.
|
||||||
|
(['nullable_1'], ['nullable_1', '-pk']),
|
||||||
|
# Related attname unique.
|
||||||
|
(['related_1_id'], ['related_1_id']),
|
||||||
|
(['related_2_id'], ['related_2_id']),
|
||||||
|
# Related ordering introspection is not implemented.
|
||||||
|
(['related_1'], ['related_1', '-pk']),
|
||||||
|
# Composite unique.
|
||||||
|
(['-field_2', 'field_3'], ['-field_2', 'field_3']),
|
||||||
|
# Composite unique nullable.
|
||||||
|
(['field_2', '-nullable_2'], ['field_2', '-nullable_2', '-pk']),
|
||||||
|
# Composite unique and nullable.
|
||||||
|
(
|
||||||
|
['field_2', '-nullable_2', 'field_3'],
|
||||||
|
['field_2', '-nullable_2', 'field_3'],
|
||||||
|
),
|
||||||
|
# Composite field and related field name.
|
||||||
|
(['field_2', '-related_3'], ['field_2', '-related_3', '-pk']),
|
||||||
|
(['field_3', 'related_4'], ['field_3', 'related_4', '-pk']),
|
||||||
|
# Composite field and related field attname.
|
||||||
|
(['field_2', 'related_3_id'], ['field_2', 'related_3_id']),
|
||||||
|
(['field_3', '-related_4_id'], ['field_3', '-related_4_id']),
|
||||||
|
# Partial unique constraint is ignored.
|
||||||
|
(['field_5'], ['field_5', '-pk']),
|
||||||
|
# Unique constraint with an empty condition.
|
||||||
|
(['field_6'], ['field_6']),
|
||||||
|
)
|
||||||
|
for ordering, expected in tests:
|
||||||
|
with self.subTest(ordering=ordering):
|
||||||
|
self.assertEqual(change_list._get_deterministic_ordering(ordering), expected)
|
||||||
|
|
||||||
def test_dynamic_list_filter(self):
|
def test_dynamic_list_filter(self):
|
||||||
"""
|
"""
|
||||||
Regression tests for ticket #17646: dynamic list_filter support.
|
Regression tests for ticket #17646: dynamic list_filter support.
|
||||||
|
|
Loading…
Reference in New Issue