diff --git a/django/forms/widgets.py b/django/forms/widgets.py index e944091f0d155..c8ec3c35d515d 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -979,7 +979,11 @@ def get_context(self, name, value, attrs): date_context['year'] = self.select_widget(attrs, choices=year_choices).get_context( name=year_name, value=context['widget']['value']['year'], - attrs={**context['widget']['attrs'], 'id': 'id_%s' % year_name}, + attrs={ + **context['widget']['attrs'], + 'id': 'id_%s' % year_name, + 'placeholder': _('Year') if self.is_required else False, + }, ) month_choices = list(self.months.items()) if not self.is_required: @@ -988,7 +992,11 @@ def get_context(self, name, value, attrs): date_context['month'] = self.select_widget(attrs, choices=month_choices).get_context( name=month_name, value=context['widget']['value']['month'], - attrs={**context['widget']['attrs'], 'id': 'id_%s' % month_name}, + attrs={ + **context['widget']['attrs'], + 'id': 'id_%s' % month_name, + 'placeholder': _('Month') if self.is_required else False, + }, ) day_choices = [(i, i) for i in range(1, 32)] if not self.is_required: @@ -997,7 +1005,11 @@ def get_context(self, name, value, attrs): date_context['day'] = self.select_widget(attrs, choices=day_choices,).get_context( name=day_name, value=context['widget']['value']['day'], - attrs={**context['widget']['attrs'], 'id': 'id_%s' % day_name}, + attrs={ + **context['widget']['attrs'], + 'id': 'id_%s' % day_name, + 'placeholder': _('Day') if self.is_required else False, + }, ) subwidgets = [] for field in self._parse_date_fmt(): diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index f43a11938a9a3..2b9c5c5ea0179 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -373,6 +373,10 @@ Miscellaneous * Support for ``pywatchman`` < 1.2.0 is removed. +* HTML rendered by :class:`~django.forms.SelectDateWidget` for required fields + now have the ``placeholder`` attribute, which mainly may require some + adjustments in tests that compare HTML. + .. _deprecated-features-3.0: Features deprecated in 3.0 diff --git a/tests/forms_tests/widget_tests/test_selectdatewidget.py b/tests/forms_tests/widget_tests/test_selectdatewidget.py index f9921af5f9cf1..00d3d8a7f2e21 100644 --- a/tests/forms_tests/widget_tests/test_selectdatewidget.py +++ b/tests/forms_tests/widget_tests/test_selectdatewidget.py @@ -314,6 +314,46 @@ class GetRequiredDate(Form): self.assertFalse(GetNotRequiredDate().fields['mydate'].widget.is_required) self.assertTrue(GetRequiredDate().fields['mydate'].widget.is_required) + def test_selectdate_required_placeholder(self): + for required in (True, False): + field = DateField(widget=SelectDateWidget(years=('2018', '2019')), required=required) + self.check_html(field.widget, 'my_date', '', html=( + """ + + + + """ % { + 'days_options': '\n'.join( + '' % (i, i) for i in range(1, 32) + ), + 'm_placeholder': 'placeholder="Month"' if required else '', + 'd_placeholder': 'placeholder="Day"' if required else '', + 'y_placeholder': 'placeholder="Year"' if required else '', + 'empty': '' if required else '', + } + )) + def test_selectdate_empty_label(self): w = SelectDateWidget(years=('2014',), empty_label='empty_label')