Fixed #8760 -- Changed ModelMultipleChoiceField to use invalid_list as a error message key.

This commit is contained in:
David Smith 2020-03-05 21:53:16 +00:00 committed by Mariusz Felisiak
parent 7d8cdad6b7
commit ccf32aca44
5 changed files with 50 additions and 8 deletions

View File

@ -2,7 +2,7 @@
Helper functions for creating Form classes from Django models Helper functions for creating Form classes from Django models
and database field objects. and database field objects.
""" """
import warnings
from itertools import chain from itertools import chain
from django.core.exceptions import ( from django.core.exceptions import (
@ -15,6 +15,7 @@ from django.forms.utils import ErrorList
from django.forms.widgets import ( from django.forms.widgets import (
HiddenInput, MultipleHiddenInput, RadioSelect, SelectMultiple, HiddenInput, MultipleHiddenInput, RadioSelect, SelectMultiple,
) )
from django.utils.deprecation import RemovedInDjango40Warning
from django.utils.text import capfirst, get_text_list from django.utils.text import capfirst, get_text_list
from django.utils.translation import gettext, gettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
@ -1291,7 +1292,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
widget = SelectMultiple widget = SelectMultiple
hidden_widget = MultipleHiddenInput hidden_widget = MultipleHiddenInput
default_error_messages = { default_error_messages = {
'list': _('Enter a list of values.'), 'invalid_list': _('Enter a list of values.'),
'invalid_choice': _('Select a valid choice. %(value)s is not one of the' 'invalid_choice': _('Select a valid choice. %(value)s is not one of the'
' available choices.'), ' available choices.'),
'invalid_pk_value': _('%(pk)s” is not a valid value.') 'invalid_pk_value': _('%(pk)s” is not a valid value.')
@ -1299,6 +1300,13 @@ class ModelMultipleChoiceField(ModelChoiceField):
def __init__(self, queryset, **kwargs): def __init__(self, queryset, **kwargs):
super().__init__(queryset, empty_label=None, **kwargs) super().__init__(queryset, empty_label=None, **kwargs)
if self.error_messages.get('list') is not None:
warnings.warn(
"The 'list' error message key is deprecated in favor of "
"'invalid_list'.",
RemovedInDjango40Warning, stacklevel=2,
)
self.error_messages['invalid_list'] = self.error_messages['list']
def to_python(self, value): def to_python(self, value):
if not value: if not value:
@ -1312,7 +1320,10 @@ class ModelMultipleChoiceField(ModelChoiceField):
elif not self.required and not value: elif not self.required and not value:
return self.queryset.none() return self.queryset.none()
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
raise ValidationError(self.error_messages['list'], code='list') raise ValidationError(
self.error_messages['invalid_list'],
code='invalid_list',
)
qs = self._check_values(value) qs = self._check_values(value)
# Since this overrides the inherited ModelChoiceField.clean # Since this overrides the inherited ModelChoiceField.clean
# we run custom validators here # we run custom validators here
@ -1333,8 +1344,8 @@ class ModelMultipleChoiceField(ModelChoiceField):
except TypeError: except TypeError:
# list of lists isn't hashable, for example # list of lists isn't hashable, for example
raise ValidationError( raise ValidationError(
self.error_messages['list'], self.error_messages['invalid_list'],
code='list', code='invalid_list',
) )
for pk in value: for pk in value:
try: try:

View File

@ -64,6 +64,8 @@ details on these changes.
* The ``length`` argument for ``django.utils.crypto.get_random_string()`` will * The ``length`` argument for ``django.utils.crypto.get_random_string()`` will
be required. be required.
* The ``list`` message for ``ModelMultipleChoiceField`` will be removed.
See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more
details on these changes. details on these changes.

View File

@ -1255,7 +1255,7 @@ generating choices. See :ref:`iterating-relationship-choices` for details.
* Normalizes to: A ``QuerySet`` of model instances. * Normalizes to: A ``QuerySet`` of model instances.
* Validates that every id in the given list of values exists in the * Validates that every id in the given list of values exists in the
queryset. queryset.
* Error message keys: ``required``, ``list``, ``invalid_choice``, * Error message keys: ``required``, ``invalid_list``, ``invalid_choice``,
``invalid_pk_value`` ``invalid_pk_value``
The ``invalid_choice`` message may contain ``%(value)s`` and the The ``invalid_choice`` message may contain ``%(value)s`` and the
@ -1285,6 +1285,10 @@ generating choices. See :ref:`iterating-relationship-choices` for details.
Same as :class:`ModelChoiceField.iterator`. Same as :class:`ModelChoiceField.iterator`.
.. deprecated:: 3.1
The ``list`` message is deprecated, use ``invalid_list`` instead.
.. _iterating-relationship-choices: .. _iterating-relationship-choices:
Iterating relationship choices Iterating relationship choices

View File

@ -557,6 +557,9 @@ Miscellaneous
* Calling ``django.utils.crypto.get_random_string()`` without a ``length`` * Calling ``django.utils.crypto.get_random_string()`` without a ``length``
argument is deprecated. argument is deprecated.
* The ``list`` message for :class:`~django.forms.ModelMultipleChoiceField` is
deprecated in favor of ``invalid_list``.
.. _removed-features-3.1: .. _removed-features-3.1:
Features removed in 3.1 Features removed in 3.1

View File

@ -7,7 +7,8 @@ from django.forms import (
SplitDateTimeField, TimeField, URLField, ValidationError, utils, SplitDateTimeField, TimeField, URLField, ValidationError, utils,
) )
from django.template import Context, Template from django.template import Context, Template
from django.test import SimpleTestCase, TestCase from django.test import SimpleTestCase, TestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango40Warning
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from ..models import ChoiceModel from ..models import ChoiceModel
@ -301,9 +302,30 @@ class ModelChoiceFieldErrorMessagesTestCase(TestCase, AssertFormErrorsMixin):
e = { e = {
'required': 'REQUIRED', 'required': 'REQUIRED',
'invalid_choice': '%(value)s IS INVALID CHOICE', 'invalid_choice': '%(value)s IS INVALID CHOICE',
'list': 'NOT A LIST OF VALUES', 'invalid_list': 'NOT A LIST OF VALUES',
} }
f = ModelMultipleChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e) f = ModelMultipleChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
self.assertFormErrors(['REQUIRED'], f.clean, '') self.assertFormErrors(['REQUIRED'], f.clean, '')
self.assertFormErrors(['NOT A LIST OF VALUES'], f.clean, '3') self.assertFormErrors(['NOT A LIST OF VALUES'], f.clean, '3')
self.assertFormErrors(['4 IS INVALID CHOICE'], f.clean, ['4']) self.assertFormErrors(['4 IS INVALID CHOICE'], f.clean, ['4'])
class DeprecationTests(TestCase, AssertFormErrorsMixin):
@ignore_warnings(category=RemovedInDjango40Warning)
def test_list_error_message(self):
f = ModelMultipleChoiceField(
queryset=ChoiceModel.objects.all(),
error_messages={'list': 'NOT A LIST OF VALUES'},
)
self.assertFormErrors(['NOT A LIST OF VALUES'], f.clean, '3')
def test_list_error_message_warning(self):
msg = (
"The 'list' error message key is deprecated in favor of "
"'invalid_list'."
)
with self.assertRaisesMessage(RemovedInDjango40Warning, msg):
ModelMultipleChoiceField(
queryset=ChoiceModel.objects.all(),
error_messages={'list': 'NOT A LIST OF VALUES'},
)