Fixed #26813 -- Prevented empty choice in ModelChoiceField with RadioSelect for fields with blank=False.
This commit is contained in:
parent
1d5fb35e6a
commit
da79ee472d
|
@ -980,6 +980,7 @@ class ForeignKey(ForeignObject):
|
||||||
'queryset': self.remote_field.model._default_manager.using(using),
|
'queryset': self.remote_field.model._default_manager.using(using),
|
||||||
'to_field_name': self.remote_field.field_name,
|
'to_field_name': self.remote_field.field_name,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
'blank': self.blank,
|
||||||
})
|
})
|
||||||
|
|
||||||
def db_check(self, connection):
|
def db_check(self, connection):
|
||||||
|
|
|
@ -13,7 +13,7 @@ from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass
|
||||||
from django.forms.formsets import BaseFormSet, formset_factory
|
from django.forms.formsets import BaseFormSet, formset_factory
|
||||||
from django.forms.utils import ErrorList
|
from django.forms.utils import ErrorList
|
||||||
from django.forms.widgets import (
|
from django.forms.widgets import (
|
||||||
HiddenInput, MultipleHiddenInput, SelectMultiple,
|
HiddenInput, MultipleHiddenInput, RadioSelect, SelectMultiple,
|
||||||
)
|
)
|
||||||
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 _
|
||||||
|
@ -1184,18 +1184,20 @@ class ModelChoiceField(ChoiceField):
|
||||||
def __init__(self, queryset, *, empty_label="---------",
|
def __init__(self, queryset, *, empty_label="---------",
|
||||||
required=True, widget=None, label=None, initial=None,
|
required=True, widget=None, label=None, initial=None,
|
||||||
help_text='', to_field_name=None, limit_choices_to=None,
|
help_text='', to_field_name=None, limit_choices_to=None,
|
||||||
**kwargs):
|
blank=False, **kwargs):
|
||||||
if required and (initial is not None):
|
|
||||||
self.empty_label = None
|
|
||||||
else:
|
|
||||||
self.empty_label = empty_label
|
|
||||||
|
|
||||||
# Call Field instead of ChoiceField __init__() because we don't need
|
# Call Field instead of ChoiceField __init__() because we don't need
|
||||||
# ChoiceField.__init__().
|
# ChoiceField.__init__().
|
||||||
Field.__init__(
|
Field.__init__(
|
||||||
self, required=required, widget=widget, label=label,
|
self, required=required, widget=widget, label=label,
|
||||||
initial=initial, help_text=help_text, **kwargs
|
initial=initial, help_text=help_text, **kwargs
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
(required and initial is not None) or
|
||||||
|
(isinstance(self.widget, RadioSelect) and not blank)
|
||||||
|
):
|
||||||
|
self.empty_label = None
|
||||||
|
else:
|
||||||
|
self.empty_label = empty_label
|
||||||
self.queryset = queryset
|
self.queryset = queryset
|
||||||
self.limit_choices_to = limit_choices_to # limit the queryset later.
|
self.limit_choices_to = limit_choices_to # limit the queryset later.
|
||||||
self.to_field_name = to_field_name
|
self.to_field_name = to_field_name
|
||||||
|
|
|
@ -393,6 +393,9 @@ class Character(models.Model):
|
||||||
username = models.CharField(max_length=100)
|
username = models.CharField(max_length=100)
|
||||||
last_action = models.DateTimeField()
|
last_action = models.DateTimeField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.username
|
||||||
|
|
||||||
|
|
||||||
class StumpJoke(models.Model):
|
class StumpJoke(models.Model):
|
||||||
most_recently_fooled = models.ForeignKey(
|
most_recently_fooled = models.ForeignKey(
|
||||||
|
|
|
@ -139,6 +139,26 @@ class ModelChoiceFieldTests(TestCase):
|
||||||
Category.objects.all().delete()
|
Category.objects.all().delete()
|
||||||
self.assertIs(bool(f.choices), True)
|
self.assertIs(bool(f.choices), True)
|
||||||
|
|
||||||
|
def test_choices_radio_blank(self):
|
||||||
|
choices = [
|
||||||
|
(self.c1.pk, 'Entertainment'),
|
||||||
|
(self.c2.pk, 'A test'),
|
||||||
|
(self.c3.pk, 'Third'),
|
||||||
|
]
|
||||||
|
categories = Category.objects.all()
|
||||||
|
for widget in [forms.RadioSelect, forms.RadioSelect()]:
|
||||||
|
for blank in [True, False]:
|
||||||
|
with self.subTest(widget=widget, blank=blank):
|
||||||
|
f = forms.ModelChoiceField(
|
||||||
|
categories,
|
||||||
|
widget=widget,
|
||||||
|
blank=blank,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
list(f.choices),
|
||||||
|
[('', '---------')] + choices if blank else choices,
|
||||||
|
)
|
||||||
|
|
||||||
def test_deepcopies_widget(self):
|
def test_deepcopies_widget(self):
|
||||||
class ModelChoiceForm(forms.Form):
|
class ModelChoiceForm(forms.Form):
|
||||||
category = forms.ModelChoiceField(Category.objects.all())
|
category = forms.ModelChoiceField(Category.objects.all())
|
||||||
|
|
|
@ -273,6 +273,23 @@ class ModelFormBaseTest(TestCase):
|
||||||
(writer.pk, 'Joe Doe'),
|
(writer.pk, 'Joe Doe'),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_non_blank_foreign_key_with_radio(self):
|
||||||
|
class AwardForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Award
|
||||||
|
fields = ['character']
|
||||||
|
widgets = {'character': forms.RadioSelect()}
|
||||||
|
|
||||||
|
character = Character.objects.create(
|
||||||
|
username='user',
|
||||||
|
last_action=datetime.datetime.today(),
|
||||||
|
)
|
||||||
|
form = AwardForm()
|
||||||
|
self.assertEqual(
|
||||||
|
list(form.fields['character'].choices),
|
||||||
|
[(character.pk, 'user')],
|
||||||
|
)
|
||||||
|
|
||||||
def test_save_blank_false_with_required_false(self):
|
def test_save_blank_false_with_required_false(self):
|
||||||
"""
|
"""
|
||||||
A ModelForm with a model with a field set to blank=False and the form
|
A ModelForm with a model with a field set to blank=False and the form
|
||||||
|
|
Loading…
Reference in New Issue