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
|
||||
ordering_fields.add(field.attname)
|
||||
else:
|
||||
# No single total ordering field, try unique_together.
|
||||
for field_names in self.lookup_opts.unique_together:
|
||||
# No single total ordering field, try unique_together and total
|
||||
# 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().
|
||||
fields = [self.lookup_opts.get_field(field_name) for field_name in field_names]
|
||||
# Composite unique constraints containing a nullable column
|
||||
|
|
|
@ -1058,6 +1058,98 @@ class ChangeListTests(TestCase):
|
|||
with self.subTest(ordering=ordering):
|
||||
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):
|
||||
"""
|
||||
Regression tests for ticket #17646: dynamic list_filter support.
|
||||
|
|
Loading…
Reference in New Issue