[3.0.x] Fixed #30014 -- Fixed ModelChoiceField validation when initial value is a model instance.

Thanks Carlton Gibson for reviews.

Backport of e7cdb0cd7e from master
This commit is contained in:
Etienne Chové 2019-03-13 10:02:50 +01:00 committed by Mariusz Felisiak
parent 82ba905db3
commit 651299e1ef
2 changed files with 21 additions and 0 deletions

View File

@ -1248,6 +1248,8 @@ class ModelChoiceField(ChoiceField):
return None return None
try: try:
key = self.to_field_name or 'pk' key = self.to_field_name or 'pk'
if isinstance(value, self.queryset.model):
value = getattr(value, key)
value = self.queryset.get(**{key: value}) value = self.queryset.get(**{key: value})
except (ValueError, TypeError, self.queryset.model.DoesNotExist): 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')

View File

@ -55,9 +55,18 @@ class ModelChoiceFieldTests(TestCase):
with self.assertRaisesMessage(ValidationError, msg): with self.assertRaisesMessage(ValidationError, msg):
f.clean(c4.id) f.clean(c4.id)
def test_clean_model_instance(self):
f = forms.ModelChoiceField(Category.objects.all())
self.assertEqual(f.clean(self.c1), self.c1)
# An instance of incorrect model.
msg = "['Select a valid choice. That choice is not one of the available choices.']"
with self.assertRaisesMessage(ValidationError, msg):
f.clean(Book.objects.create())
def test_clean_to_field_name(self): def test_clean_to_field_name(self):
f = forms.ModelChoiceField(Category.objects.all(), to_field_name='slug') f = forms.ModelChoiceField(Category.objects.all(), to_field_name='slug')
self.assertEqual(f.clean(self.c1.slug), self.c1) self.assertEqual(f.clean(self.c1.slug), self.c1)
self.assertEqual(f.clean(self.c1), self.c1)
def test_choices(self): def test_choices(self):
f = forms.ModelChoiceField(Category.objects.filter(pk=self.c1.id), required=False) f = forms.ModelChoiceField(Category.objects.filter(pk=self.c1.id), required=False)
@ -194,6 +203,16 @@ class ModelChoiceFieldTests(TestCase):
field = forms.ModelChoiceField(Author.objects.all(), disabled=True) field = forms.ModelChoiceField(Author.objects.all(), disabled=True)
self.assertIs(field.has_changed('x', 'y'), False) self.assertIs(field.has_changed('x', 'y'), False)
def test_disabled_modelchoicefield_initial_model_instance(self):
class ModelChoiceForm(forms.Form):
categories = forms.ModelChoiceField(
Category.objects.all(),
disabled=True,
initial=self.c1,
)
self.assertTrue(ModelChoiceForm(data={'categories': self.c1.pk}).is_valid())
def test_disabled_multiplemodelchoicefield(self): def test_disabled_multiplemodelchoicefield(self):
class ArticleForm(forms.ModelForm): class ArticleForm(forms.ModelForm):
categories = forms.ModelMultipleChoiceField(Category.objects.all(), required=False) categories = forms.ModelMultipleChoiceField(Category.objects.all(), required=False)