From 1adc09064f88b69b3b565b57502f2a0a32b5f3a8 Mon Sep 17 00:00:00 2001 From: Jerin Peter George Date: Tue, 26 Jan 2021 12:05:20 +0530 Subject: [PATCH] Fixed #32347 -- Made ModelChoiceField include the value in ValidationError for invalid_choice. --- AUTHORS | 1 + django/forms/models.py | 6 +++++- docs/ref/forms/fields.txt | 8 ++++++++ docs/releases/4.0.txt | 6 +++++- tests/forms_tests/tests/test_error_messages.py | 13 +++++++++++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index a92baaceb9..f5c71c7e30 100644 --- a/AUTHORS +++ b/AUTHORS @@ -442,6 +442,7 @@ answer newbie questions, and generally made Django that much better: Jeremy Carbaugh Jeremy Dunck Jeremy Lainé + Jerin Peter George Jesse Young Jezeniel Zapanta jhenry diff --git a/django/forms/models.py b/django/forms/models.py index 422bc5d178..a88c384841 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -1284,7 +1284,11 @@ class ModelChoiceField(ChoiceField): value = getattr(value, key) value = self.queryset.get(**{key: value}) except (ValueError, TypeError, self.queryset.model.DoesNotExist): - raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') + raise ValidationError( + self.error_messages['invalid_choice'], + code='invalid_choice', + params={'value': value}, + ) return value def validate(self, value): diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index b13af1a270..a4f80831ad 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -1219,6 +1219,9 @@ generating choices. See :ref:`iterating-relationship-choices` for details. * Validates that the given id exists in the queryset. * Error message keys: ``required``, ``invalid_choice`` + The ``invalid_choice`` error message may contain ``%(value)s``, which will + be replaced with the selected choice. + Allows the selection of a single model object, suitable for representing a foreign key. Note that the default widget for ``ModelChoiceField`` becomes impractical when the number of entries increases. You should avoid using it @@ -1307,6 +1310,11 @@ generating choices. See :ref:`iterating-relationship-choices` for details. def label_from_instance(self, obj): return "My Object #%i" % obj.id + .. versionchanged:: 4.0 + + Support for containing ``%(value)s`` in the ``invalid_choice`` error + message was added. + ``ModelMultipleChoiceField`` ---------------------------- diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt index 1e408c97bd..99ad826865 100644 --- a/docs/releases/4.0.txt +++ b/docs/releases/4.0.txt @@ -135,7 +135,11 @@ File Uploads Forms ~~~~~ -* ... +* :class:`~django.forms.ModelChoiceField` now includes the provided value in + the ``params`` argument of a raised + :exc:`~django.core.exceptions.ValidationError` for the ``invalid_choice`` + error message. This allows custom error messages to use the ``%(value)s`` + placeholder. Generic Views ~~~~~~~~~~~~~ diff --git a/tests/forms_tests/tests/test_error_messages.py b/tests/forms_tests/tests/test_error_messages.py index 1a6d1386c3..340c39546c 100644 --- a/tests/forms_tests/tests/test_error_messages.py +++ b/tests/forms_tests/tests/test_error_messages.py @@ -308,3 +308,16 @@ class ModelChoiceFieldErrorMessagesTestCase(TestCase, AssertFormErrorsMixin): self.assertFormErrors(['REQUIRED'], f.clean, '') self.assertFormErrors(['NOT A LIST OF VALUES'], f.clean, '3') self.assertFormErrors(['4 IS INVALID CHOICE'], f.clean, ['4']) + + def test_modelchoicefield_value_placeholder(self): + f = ModelChoiceField( + queryset=ChoiceModel.objects.all(), + error_messages={ + 'invalid_choice': '"%(value)s" is not one of the available choices.', + }, + ) + self.assertFormErrors( + ['"invalid" is not one of the available choices.'], + f.clean, + 'invalid', + )