Fixed #25320 -- Reverted ManyToManyField.null to False for backwards compatibility.
Thanks Tom Christie for the report and review.
This commit is contained in:
parent
4c30fa905d
commit
064d4b1cb0
|
@ -176,8 +176,16 @@ class RelatedFieldListFilter(FieldListFilter):
|
||||||
self.title = self.lookup_title
|
self.title = self.lookup_title
|
||||||
self.empty_value_display = model_admin.get_empty_value_display()
|
self.empty_value_display = model_admin.get_empty_value_display()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def include_empty_choice(self):
|
||||||
|
"""
|
||||||
|
Return True if a "(None)" choice should be included, which filters
|
||||||
|
out everything except empty relationships.
|
||||||
|
"""
|
||||||
|
return self.field.null or (self.field.is_relation and self.field.many_to_many)
|
||||||
|
|
||||||
def has_output(self):
|
def has_output(self):
|
||||||
if self.field.null:
|
if self.include_empty_choice:
|
||||||
extra = 1
|
extra = 1
|
||||||
else:
|
else:
|
||||||
extra = 0
|
extra = 0
|
||||||
|
@ -204,7 +212,7 @@ class RelatedFieldListFilter(FieldListFilter):
|
||||||
}, [self.lookup_kwarg_isnull]),
|
}, [self.lookup_kwarg_isnull]),
|
||||||
'display': val,
|
'display': val,
|
||||||
}
|
}
|
||||||
if self.field.null:
|
if self.include_empty_choice:
|
||||||
yield {
|
yield {
|
||||||
'selected': bool(self.lookup_val_isnull),
|
'selected': bool(self.lookup_val_isnull),
|
||||||
'query_string': cl.get_query_string({
|
'query_string': cl.get_query_string({
|
||||||
|
|
|
@ -2304,8 +2304,6 @@ class ManyToManyField(RelatedField):
|
||||||
|
|
||||||
self.db_table = db_table
|
self.db_table = db_table
|
||||||
self.swappable = swappable
|
self.swappable = swappable
|
||||||
# Many-to-many fields are always nullable.
|
|
||||||
self.null = True
|
|
||||||
|
|
||||||
def check(self, **kwargs):
|
def check(self, **kwargs):
|
||||||
errors = super(ManyToManyField, self).check(**kwargs)
|
errors = super(ManyToManyField, self).check(**kwargs)
|
||||||
|
@ -2552,7 +2550,6 @@ class ManyToManyField(RelatedField):
|
||||||
def deconstruct(self):
|
def deconstruct(self):
|
||||||
name, path, args, kwargs = super(ManyToManyField, self).deconstruct()
|
name, path, args, kwargs = super(ManyToManyField, self).deconstruct()
|
||||||
# Handle the simpler arguments.
|
# Handle the simpler arguments.
|
||||||
del kwargs["null"]
|
|
||||||
if self.db_table is not None:
|
if self.db_table is not None:
|
||||||
kwargs['db_table'] = self.db_table
|
kwargs['db_table'] = self.db_table
|
||||||
if self.remote_field.db_constraint is not True:
|
if self.remote_field.db_constraint is not True:
|
||||||
|
|
|
@ -523,6 +523,16 @@ class ListFiltersTests(TestCase):
|
||||||
self.assertEqual(choice['selected'], True)
|
self.assertEqual(choice['selected'], True)
|
||||||
self.assertEqual(choice['query_string'], '?books_contributed__id__exact=%d' % self.django_book.pk)
|
self.assertEqual(choice['query_string'], '?books_contributed__id__exact=%d' % self.django_book.pk)
|
||||||
|
|
||||||
|
# With one book, the list filter should appear because there is also a
|
||||||
|
# (None) option.
|
||||||
|
Book.objects.exclude(pk=self.djangonaut_book.pk).delete()
|
||||||
|
filterspec = changelist.get_filters(request)[0]
|
||||||
|
self.assertEqual(len(filterspec), 2)
|
||||||
|
# With no books remaining, no list filters should appear.
|
||||||
|
Book.objects.all().delete()
|
||||||
|
filterspec = changelist.get_filters(request)[0]
|
||||||
|
self.assertEqual(len(filterspec), 0)
|
||||||
|
|
||||||
def test_relatedonlyfieldlistfilter_foreignkey(self):
|
def test_relatedonlyfieldlistfilter_foreignkey(self):
|
||||||
modeladmin = BookAdminRelatedOnlyFilter(Book, site)
|
modeladmin = BookAdminRelatedOnlyFilter(Book, site)
|
||||||
|
|
||||||
|
|
|
@ -216,3 +216,9 @@ class FieldFlagsTests(test.SimpleTestCase):
|
||||||
reverse_field = field.remote_field
|
reverse_field = field.remote_field
|
||||||
self.assertEqual(field.model, reverse_field.related_model)
|
self.assertEqual(field.model, reverse_field.related_model)
|
||||||
self.assertEqual(field.related_model, reverse_field.model)
|
self.assertEqual(field.related_model, reverse_field.model)
|
||||||
|
|
||||||
|
def test_null(self):
|
||||||
|
# null isn't well defined for a ManyToManyField, but changing it to
|
||||||
|
# True causes backwards compatibility problems (#25320).
|
||||||
|
self.assertFalse(AllFieldsModel._meta.get_field('m2m').null)
|
||||||
|
self.assertTrue(AllFieldsModel._meta.get_field('reverse2').null)
|
||||||
|
|
Loading…
Reference in New Issue