summaryrefslogtreecommitdiff
path: root/django/forms/widgets.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/forms/widgets.py')
-rw-r--r--django/forms/widgets.py514
1 files changed, 299 insertions, 215 deletions
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 05667f8e44..8c5122ad1d 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -17,24 +17,41 @@ from django.utils.formats import get_format
from django.utils.html import format_html, html_safe
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import mark_safe
-from django.utils.topological_sort import (
- CyclicDependencyError, stable_topological_sort,
-)
+from django.utils.topological_sort import CyclicDependencyError, stable_topological_sort
from django.utils.translation import gettext_lazy as _
from .renderers import get_default_renderer
__all__ = (
- 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'NumberInput',
- 'EmailInput', 'URLInput', 'PasswordInput', 'HiddenInput',
- 'MultipleHiddenInput', 'FileInput', 'ClearableFileInput', 'Textarea',
- 'DateInput', 'DateTimeInput', 'TimeInput', 'CheckboxInput', 'Select',
- 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
- 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
- 'SplitHiddenDateTimeWidget', 'SelectDateWidget',
+ "Media",
+ "MediaDefiningClass",
+ "Widget",
+ "TextInput",
+ "NumberInput",
+ "EmailInput",
+ "URLInput",
+ "PasswordInput",
+ "HiddenInput",
+ "MultipleHiddenInput",
+ "FileInput",
+ "ClearableFileInput",
+ "Textarea",
+ "DateInput",
+ "DateTimeInput",
+ "TimeInput",
+ "CheckboxInput",
+ "Select",
+ "NullBooleanSelect",
+ "SelectMultiple",
+ "RadioSelect",
+ "CheckboxSelectMultiple",
+ "MultiWidget",
+ "SplitDateTimeWidget",
+ "SplitHiddenDateTimeWidget",
+ "SelectDateWidget",
)
-MEDIA_TYPES = ('css', 'js')
+MEDIA_TYPES = ("css", "js")
class MediaOrderConflictWarning(RuntimeWarning):
@@ -45,8 +62,8 @@ class MediaOrderConflictWarning(RuntimeWarning):
class Media:
def __init__(self, media=None, css=None, js=None):
if media is not None:
- css = getattr(media, 'css', {})
- js = getattr(media, 'js', [])
+ css = getattr(media, "css", {})
+ js = getattr(media, "js", [])
else:
if css is None:
css = {}
@@ -56,7 +73,7 @@ class Media:
self._js_lists = [js]
def __repr__(self):
- return 'Media(css=%r, js=%r)' % (self._css, self._js)
+ return "Media(css=%r, js=%r)" % (self._css, self._js)
def __str__(self):
return self.render()
@@ -74,26 +91,35 @@ class Media:
return self.merge(*self._js_lists)
def render(self):
- return mark_safe('\n'.join(chain.from_iterable(getattr(self, 'render_' + name)() for name in MEDIA_TYPES)))
+ return mark_safe(
+ "\n".join(
+ chain.from_iterable(
+ getattr(self, "render_" + name)() for name in MEDIA_TYPES
+ )
+ )
+ )
def render_js(self):
return [
- format_html(
- '<script src="{}"></script>',
- self.absolute_path(path)
- ) for path in self._js
+ format_html('<script src="{}"></script>', self.absolute_path(path))
+ for path in self._js
]
def render_css(self):
# To keep rendering order consistent, we can't just iterate over items().
# We need to sort the keys, and iterate over the sorted list.
media = sorted(self._css)
- return chain.from_iterable([
- format_html(
- '<link href="{}" media="{}" rel="stylesheet">',
- self.absolute_path(path), medium
- ) for path in self._css[medium]
- ] for medium in media)
+ return chain.from_iterable(
+ [
+ format_html(
+ '<link href="{}" media="{}" rel="stylesheet">',
+ self.absolute_path(path),
+ medium,
+ )
+ for path in self._css[medium]
+ ]
+ for medium in media
+ )
def absolute_path(self, path):
"""
@@ -101,14 +127,14 @@ class Media:
path. An absolute path will be returned unchanged while a relative path
will be passed to django.templatetags.static.static().
"""
- if path.startswith(('http://', 'https://', '/')):
+ if path.startswith(("http://", "https://", "/")):
return path
return static(path)
def __getitem__(self, name):
"""Return a Media object that only contains media of the given type."""
if name in MEDIA_TYPES:
- return Media(**{str(name): getattr(self, '_' + name)})
+ return Media(**{str(name): getattr(self, "_" + name)})
raise KeyError('Unknown media type "%s"' % name)
@staticmethod
@@ -138,9 +164,10 @@ class Media:
return stable_topological_sort(all_items, dependency_graph)
except CyclicDependencyError:
warnings.warn(
- 'Detected duplicate Media files in an opposite order: {}'.format(
- ', '.join(repr(list_) for list_ in lists)
- ), MediaOrderConflictWarning,
+ "Detected duplicate Media files in an opposite order: {}".format(
+ ", ".join(repr(list_) for list_ in lists)
+ ),
+ MediaOrderConflictWarning,
)
return list(all_items)
@@ -167,9 +194,9 @@ def media_property(cls):
base = Media()
# Get the media definition for this class
- definition = getattr(cls, 'Media', None)
+ definition = getattr(cls, "Media", None)
if definition:
- extend = getattr(definition, 'extend', True)
+ extend = getattr(definition, "extend", True)
if extend:
if extend is True:
m = base
@@ -180,6 +207,7 @@ def media_property(cls):
return m + Media(definition)
return Media(definition)
return base
+
return property(_media)
@@ -187,10 +215,11 @@ class MediaDefiningClass(type):
"""
Metaclass for classes that can have media definitions.
"""
+
def __new__(mcs, name, bases, attrs):
new_class = super().__new__(mcs, name, bases, attrs)
- if 'media' not in attrs:
+ if "media" not in attrs:
new_class.media = media_property(new_class)
return new_class
@@ -213,17 +242,17 @@ class Widget(metaclass=MediaDefiningClass):
@property
def is_hidden(self):
- return self.input_type == 'hidden' if hasattr(self, 'input_type') else False
+ return self.input_type == "hidden" if hasattr(self, "input_type") else False
def subwidgets(self, name, value, attrs=None):
context = self.get_context(name, value, attrs)
- yield context['widget']
+ yield context["widget"]
def format_value(self, value):
"""
Return a value as it should appear when rendered in a template.
"""
- if value == '' or value is None:
+ if value == "" or value is None:
return None
if self.is_localized:
return formats.localize_input(value)
@@ -231,13 +260,13 @@ class Widget(metaclass=MediaDefiningClass):
def get_context(self, name, value, attrs):
return {
- 'widget': {
- 'name': name,
- 'is_hidden': self.is_hidden,
- 'required': self.is_required,
- 'value': self.format_value(value),
- 'attrs': self.build_attrs(self.attrs, attrs),
- 'template_name': self.template_name,
+ "widget": {
+ "name": name,
+ "is_hidden": self.is_hidden,
+ "required": self.is_required,
+ "value": self.format_value(value),
+ "attrs": self.build_attrs(self.attrs, attrs),
+ "template_name": self.template_name,
},
}
@@ -285,44 +314,45 @@ class Input(Widget):
"""
Base class for all <input> widgets.
"""
+
input_type = None # Subclasses must define this.
- template_name = 'django/forms/widgets/input.html'
+ template_name = "django/forms/widgets/input.html"
def __init__(self, attrs=None):
if attrs is not None:
attrs = attrs.copy()
- self.input_type = attrs.pop('type', self.input_type)
+ self.input_type = attrs.pop("type", self.input_type)
super().__init__(attrs)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
- context['widget']['type'] = self.input_type
+ context["widget"]["type"] = self.input_type
return context
class TextInput(Input):
- input_type = 'text'
- template_name = 'django/forms/widgets/text.html'
+ input_type = "text"
+ template_name = "django/forms/widgets/text.html"
class NumberInput(Input):
- input_type = 'number'
- template_name = 'django/forms/widgets/number.html'
+ input_type = "number"
+ template_name = "django/forms/widgets/number.html"
class EmailInput(Input):
- input_type = 'email'
- template_name = 'django/forms/widgets/email.html'
+ input_type = "email"
+ template_name = "django/forms/widgets/email.html"
class URLInput(Input):
- input_type = 'url'
- template_name = 'django/forms/widgets/url.html'
+ input_type = "url"
+ template_name = "django/forms/widgets/url.html"
class PasswordInput(Input):
- input_type = 'password'
- template_name = 'django/forms/widgets/password.html'
+ input_type = "password"
+ template_name = "django/forms/widgets/password.html"
def __init__(self, attrs=None, render_value=False):
super().__init__(attrs)
@@ -335,8 +365,8 @@ class PasswordInput(Input):
class HiddenInput(Input):
- input_type = 'hidden'
- template_name = 'django/forms/widgets/hidden.html'
+ input_type = "hidden"
+ template_name = "django/forms/widgets/hidden.html"
class MultipleHiddenInput(HiddenInput):
@@ -344,25 +374,26 @@ class MultipleHiddenInput(HiddenInput):
Handle <input type="hidden"> for fields that have a list
of values.
"""
- template_name = 'django/forms/widgets/multiple_hidden.html'
+
+ template_name = "django/forms/widgets/multiple_hidden.html"
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
- final_attrs = context['widget']['attrs']
- id_ = context['widget']['attrs'].get('id')
+ final_attrs = context["widget"]["attrs"]
+ id_ = context["widget"]["attrs"].get("id")
subwidgets = []
- for index, value_ in enumerate(context['widget']['value']):
+ for index, value_ in enumerate(context["widget"]["value"]):
widget_attrs = final_attrs.copy()
if id_:
# An ID attribute was given. Add a numeric index as a suffix
# so that the inputs don't all have the same ID attribute.
- widget_attrs['id'] = '%s_%s' % (id_, index)
+ widget_attrs["id"] = "%s_%s" % (id_, index)
widget = HiddenInput()
widget.is_required = self.is_required
- subwidgets.append(widget.get_context(name, value_, widget_attrs)['widget'])
+ subwidgets.append(widget.get_context(name, value_, widget_attrs)["widget"])
- context['widget']['subwidgets'] = subwidgets
+ context["widget"]["subwidgets"] = subwidgets
return context
def value_from_datadict(self, data, files, name):
@@ -377,9 +408,9 @@ class MultipleHiddenInput(HiddenInput):
class FileInput(Input):
- input_type = 'file'
+ input_type = "file"
needs_multipart_form = True
- template_name = 'django/forms/widgets/file.html'
+ template_name = "django/forms/widgets/file.html"
def format_value(self, value):
"""File input never renders a value."""
@@ -400,29 +431,29 @@ FILE_INPUT_CONTRADICTION = object()
class ClearableFileInput(FileInput):
- clear_checkbox_label = _('Clear')
- initial_text = _('Currently')
- input_text = _('Change')
- template_name = 'django/forms/widgets/clearable_file_input.html'
+ clear_checkbox_label = _("Clear")
+ initial_text = _("Currently")
+ input_text = _("Change")
+ template_name = "django/forms/widgets/clearable_file_input.html"
def clear_checkbox_name(self, name):
"""
Given the name of the file input, return the name of the clear checkbox
input.
"""
- return name + '-clear'
+ return name + "-clear"
def clear_checkbox_id(self, name):
"""
Given the name of the clear checkbox input, return the HTML id for it.
"""
- return name + '_id'
+ return name + "_id"
def is_initial(self, value):
"""
Return whether value is considered to be initial value.
"""
- return bool(value and getattr(value, 'url', False))
+ return bool(value and getattr(value, "url", False))
def format_value(self, value):
"""
@@ -435,20 +466,23 @@ class ClearableFileInput(FileInput):
context = super().get_context(name, value, attrs)
checkbox_name = self.clear_checkbox_name(name)
checkbox_id = self.clear_checkbox_id(checkbox_name)
- context['widget'].update({
- 'checkbox_name': checkbox_name,
- 'checkbox_id': checkbox_id,
- 'is_initial': self.is_initial(value),
- 'input_text': self.input_text,
- 'initial_text': self.initial_text,
- 'clear_checkbox_label': self.clear_checkbox_label,
- })
+ context["widget"].update(
+ {
+ "checkbox_name": checkbox_name,
+ "checkbox_id": checkbox_id,
+ "is_initial": self.is_initial(value),
+ "input_text": self.input_text,
+ "initial_text": self.initial_text,
+ "clear_checkbox_label": self.clear_checkbox_label,
+ }
+ )
return context
def value_from_datadict(self, data, files, name):
upload = super().value_from_datadict(data, files, name)
if not self.is_required and CheckboxInput().value_from_datadict(
- data, files, self.clear_checkbox_name(name)):
+ data, files, self.clear_checkbox_name(name)
+ ):
if upload:
# If the user contradicts themselves (uploads a new file AND
@@ -461,24 +495,24 @@ class ClearableFileInput(FileInput):
def value_omitted_from_data(self, data, files, name):
return (
- super().value_omitted_from_data(data, files, name) and
- self.clear_checkbox_name(name) not in data
+ super().value_omitted_from_data(data, files, name)
+ and self.clear_checkbox_name(name) not in data
)
class Textarea(Widget):
- template_name = 'django/forms/widgets/textarea.html'
+ template_name = "django/forms/widgets/textarea.html"
def __init__(self, attrs=None):
# Use slightly better defaults than HTML's 20x2 box
- default_attrs = {'cols': '40', 'rows': '10'}
+ default_attrs = {"cols": "40", "rows": "10"}
if attrs:
default_attrs.update(attrs)
super().__init__(default_attrs)
class DateTimeBaseInput(TextInput):
- format_key = ''
+ format_key = ""
supports_microseconds = False
def __init__(self, attrs=None, format=None):
@@ -486,32 +520,34 @@ class DateTimeBaseInput(TextInput):
self.format = format or None
def format_value(self, value):
- return formats.localize_input(value, self.format or formats.get_format(self.format_key)[0])
+ return formats.localize_input(
+ value, self.format or formats.get_format(self.format_key)[0]
+ )
class DateInput(DateTimeBaseInput):
- format_key = 'DATE_INPUT_FORMATS'
- template_name = 'django/forms/widgets/date.html'
+ format_key = "DATE_INPUT_FORMATS"
+ template_name = "django/forms/widgets/date.html"
class DateTimeInput(DateTimeBaseInput):
- format_key = 'DATETIME_INPUT_FORMATS'
- template_name = 'django/forms/widgets/datetime.html'
+ format_key = "DATETIME_INPUT_FORMATS"
+ template_name = "django/forms/widgets/datetime.html"
class TimeInput(DateTimeBaseInput):
- format_key = 'TIME_INPUT_FORMATS'
- template_name = 'django/forms/widgets/time.html'
+ format_key = "TIME_INPUT_FORMATS"
+ template_name = "django/forms/widgets/time.html"
# Defined at module level so that CheckboxInput is picklable (#17976)
def boolean_check(v):
- return not (v is False or v is None or v == '')
+ return not (v is False or v is None or v == "")
class CheckboxInput(Input):
- input_type = 'checkbox'
- template_name = 'django/forms/widgets/checkbox.html'
+ input_type = "checkbox"
+ template_name = "django/forms/widgets/checkbox.html"
def __init__(self, attrs=None, check_test=None):
super().__init__(attrs)
@@ -521,13 +557,13 @@ class CheckboxInput(Input):
def format_value(self, value):
"""Only return the 'value' attribute if value isn't empty."""
- if value is True or value is False or value is None or value == '':
+ if value is True or value is False or value is None or value == "":
return
return str(value)
def get_context(self, name, value, attrs):
if self.check_test(value):
- attrs = {**(attrs or {}), 'checked': True}
+ attrs = {**(attrs or {}), "checked": True}
return super().get_context(name, value, attrs)
def value_from_datadict(self, data, files, name):
@@ -537,7 +573,7 @@ class CheckboxInput(Input):
return False
value = data.get(name)
# Translate true and false strings to boolean values.
- values = {'true': True, 'false': False}
+ values = {"true": True, "false": False}
if isinstance(value, str):
value = values.get(value.lower(), value)
return bool(value)
@@ -554,7 +590,7 @@ class ChoiceWidget(Widget):
template_name = None
option_template_name = None
add_id_index = True
- checked_attribute = {'checked': True}
+ checked_attribute = {"checked": True}
option_inherits_attrs = True
def __init__(self, attrs=None, choices=()):
@@ -591,7 +627,7 @@ class ChoiceWidget(Widget):
for index, (option_value, option_label) in enumerate(self.choices):
if option_value is None:
- option_value = ''
+ option_value = ""
subgroup = []
if isinstance(option_label, (list, tuple)):
@@ -605,50 +641,62 @@ class ChoiceWidget(Widget):
groups.append((group_name, subgroup, index))
for subvalue, sublabel in choices:
- selected = (
- (not has_selected or self.allow_multiple_selected) and
- str(subvalue) in value
- )
+ selected = (not has_selected or self.allow_multiple_selected) and str(
+ subvalue
+ ) in value
has_selected |= selected
- subgroup.append(self.create_option(
- name, subvalue, sublabel, selected, index,
- subindex=subindex, attrs=attrs,
- ))
+ subgroup.append(
+ self.create_option(
+ name,
+ subvalue,
+ sublabel,
+ selected,
+ index,
+ subindex=subindex,
+ attrs=attrs,
+ )
+ )
if subindex is not None:
subindex += 1
return groups
- def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
+ def create_option(
+ self, name, value, label, selected, index, subindex=None, attrs=None
+ ):
index = str(index) if subindex is None else "%s_%s" % (index, subindex)
- option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
+ option_attrs = (
+ self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
+ )
if selected:
option_attrs.update(self.checked_attribute)
- if 'id' in option_attrs:
- option_attrs['id'] = self.id_for_label(option_attrs['id'], index)
+ if "id" in option_attrs:
+ option_attrs["id"] = self.id_for_label(option_attrs["id"], index)
return {
- 'name': name,
- 'value': value,
- 'label': label,
- 'selected': selected,
- 'index': index,
- 'attrs': option_attrs,
- 'type': self.input_type,
- 'template_name': self.option_template_name,
- 'wrap_label': True,
+ "name": name,
+ "value": value,
+ "label": label,
+ "selected": selected,
+ "index": index,
+ "attrs": option_attrs,
+ "type": self.input_type,
+ "template_name": self.option_template_name,
+ "wrap_label": True,
}
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
- context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs)
+ context["widget"]["optgroups"] = self.optgroups(
+ name, context["widget"]["value"], attrs
+ )
return context
- def id_for_label(self, id_, index='0'):
+ def id_for_label(self, id_, index="0"):
"""
Use an incremented id for each option where the main widget
references the zero index.
"""
if id_ and self.add_id_index:
- id_ = '%s_%s' % (id_, index)
+ id_ = "%s_%s" % (id_, index)
return id_
def value_from_datadict(self, data, files, name):
@@ -666,28 +714,28 @@ class ChoiceWidget(Widget):
return []
if not isinstance(value, (tuple, list)):
value = [value]
- return [str(v) if v is not None else '' for v in value]
+ return [str(v) if v is not None else "" for v in value]
class Select(ChoiceWidget):
- input_type = 'select'
- template_name = 'django/forms/widgets/select.html'
- option_template_name = 'django/forms/widgets/select_option.html'
+ input_type = "select"
+ template_name = "django/forms/widgets/select.html"
+ option_template_name = "django/forms/widgets/select_option.html"
add_id_index = False
- checked_attribute = {'selected': True}
+ checked_attribute = {"selected": True}
option_inherits_attrs = False
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
if self.allow_multiple_selected:
- context['widget']['attrs']['multiple'] = True
+ context["widget"]["attrs"]["multiple"] = True
return context
@staticmethod
def _choice_has_empty_value(choice):
"""Return True if the choice's value is empty string or None."""
value, _ = choice
- return value is None or value == ''
+ return value is None or value == ""
def use_required_attribute(self, initial):
"""
@@ -700,44 +748,52 @@ class Select(ChoiceWidget):
return use_required_attribute
first_choice = next(iter(self.choices), None)
- return use_required_attribute and first_choice is not None and self._choice_has_empty_value(first_choice)
+ return (
+ use_required_attribute
+ and first_choice is not None
+ and self._choice_has_empty_value(first_choice)
+ )
class NullBooleanSelect(Select):
"""
A Select Widget intended to be used with NullBooleanField.
"""
+
def __init__(self, attrs=None):
choices = (
- ('unknown', _('Unknown')),
- ('true', _('Yes')),
- ('false', _('No')),
+ ("unknown", _("Unknown")),
+ ("true", _("Yes")),
+ ("false", _("No")),
)
super().__init__(attrs, choices)
def format_value(self, value):
try:
return {
- True: 'true', False: 'false',
- 'true': 'true', 'false': 'false',
+ True: "true",
+ False: "false",
+ "true": "true",
+ "false": "false",
# For backwards compatibility with Django < 2.2.
- '2': 'true', '3': 'false',
+ "2": "true",
+ "3": "false",
}[value]
except KeyError:
- return 'unknown'
+ return "unknown"
def value_from_datadict(self, data, files, name):
value = data.get(name)
return {
True: True,
- 'True': True,
- 'False': False,
+ "True": True,
+ "False": False,
False: False,
- 'true': True,
- 'false': False,
+ "true": True,
+ "false": False,
# For backwards compatibility with Django < 2.2.
- '2': True,
- '3': False,
+ "2": True,
+ "3": False,
}.get(value)
@@ -758,9 +814,9 @@ class SelectMultiple(Select):
class RadioSelect(ChoiceWidget):
- input_type = 'radio'
- template_name = 'django/forms/widgets/radio.html'
- option_template_name = 'django/forms/widgets/radio_option.html'
+ input_type = "radio"
+ template_name = "django/forms/widgets/radio.html"
+ option_template_name = "django/forms/widgets/radio_option.html"
def id_for_label(self, id_, index=None):
"""
@@ -769,15 +825,15 @@ class RadioSelect(ChoiceWidget):
the first input.
"""
if index is None:
- return ''
+ return ""
return super().id_for_label(id_, index)
class CheckboxSelectMultiple(RadioSelect):
allow_multiple_selected = True
- input_type = 'checkbox'
- template_name = 'django/forms/widgets/checkbox_select.html'
- option_template_name = 'django/forms/widgets/checkbox_option.html'
+ input_type = "checkbox"
+ template_name = "django/forms/widgets/checkbox_select.html"
+ option_template_name = "django/forms/widgets/checkbox_option.html"
def use_required_attribute(self, initial):
# Don't use the 'required' attribute because browser validation would
@@ -800,16 +856,15 @@ class MultiWidget(Widget):
You'll probably want to use this class with MultiValueField.
"""
- template_name = 'django/forms/widgets/multiwidget.html'
+
+ template_name = "django/forms/widgets/multiwidget.html"
def __init__(self, widgets, attrs=None):
if isinstance(widgets, dict):
- self.widgets_names = [
- ('_%s' % name) if name else '' for name in widgets
- ]
+ self.widgets_names = [("_%s" % name) if name else "" for name in widgets]
widgets = widgets.values()
else:
- self.widgets_names = ['_%s' % i for i in range(len(widgets))]
+ self.widgets_names = ["_%s" % i for i in range(len(widgets))]
self.widgets = [w() if isinstance(w, type) else w for w in widgets]
super().__init__(attrs)
@@ -827,11 +882,13 @@ class MultiWidget(Widget):
if not isinstance(value, list):
value = self.decompress(value)
- final_attrs = context['widget']['attrs']
- input_type = final_attrs.pop('type', None)
- id_ = final_attrs.get('id')
+ final_attrs = context["widget"]["attrs"]
+ input_type = final_attrs.pop("type", None)
+ id_ = final_attrs.get("id")
subwidgets = []
- for i, (widget_name, widget) in enumerate(zip(self.widgets_names, self.widgets)):
+ for i, (widget_name, widget) in enumerate(
+ zip(self.widgets_names, self.widgets)
+ ):
if input_type is not None:
widget.input_type = input_type
widget_name = name + widget_name
@@ -841,15 +898,17 @@ class MultiWidget(Widget):
widget_value = None
if id_:
widget_attrs = final_attrs.copy()
- widget_attrs['id'] = '%s_%s' % (id_, i)
+ widget_attrs["id"] = "%s_%s" % (id_, i)
else:
widget_attrs = final_attrs
- subwidgets.append(widget.get_context(widget_name, widget_value, widget_attrs)['widget'])
- context['widget']['subwidgets'] = subwidgets
+ subwidgets.append(
+ widget.get_context(widget_name, widget_value, widget_attrs)["widget"]
+ )
+ context["widget"]["subwidgets"] = subwidgets
return context
def id_for_label(self, id_):
- return ''
+ return ""
def value_from_datadict(self, data, files, name):
return [
@@ -869,7 +928,7 @@ class MultiWidget(Widget):
The given value can be assumed to be valid, but not necessarily
non-empty.
"""
- raise NotImplementedError('Subclasses must implement this method.')
+ raise NotImplementedError("Subclasses must implement this method.")
def _get_media(self):
"""
@@ -880,6 +939,7 @@ class MultiWidget(Widget):
for w in self.widgets:
media = media + w.media
return media
+
media = property(_get_media)
def __deepcopy__(self, memo):
@@ -896,10 +956,18 @@ class SplitDateTimeWidget(MultiWidget):
"""
A widget that splits datetime input into two <input type="text"> boxes.
"""
+
supports_microseconds = False
- template_name = 'django/forms/widgets/splitdatetime.html'
+ template_name = "django/forms/widgets/splitdatetime.html"
- def __init__(self, attrs=None, date_format=None, time_format=None, date_attrs=None, time_attrs=None):
+ def __init__(
+ self,
+ attrs=None,
+ date_format=None,
+ time_format=None,
+ date_attrs=None,
+ time_attrs=None,
+ ):
widgets = (
DateInput(
attrs=attrs if date_attrs is None else date_attrs,
@@ -923,12 +991,20 @@ class SplitHiddenDateTimeWidget(SplitDateTimeWidget):
"""
A widget that splits datetime input into two <input type="hidden"> inputs.
"""
- template_name = 'django/forms/widgets/splithiddendatetime.html'
- def __init__(self, attrs=None, date_format=None, time_format=None, date_attrs=None, time_attrs=None):
+ template_name = "django/forms/widgets/splithiddendatetime.html"
+
+ def __init__(
+ self,
+ attrs=None,
+ date_format=None,
+ time_format=None,
+ date_attrs=None,
+ time_attrs=None,
+ ):
super().__init__(attrs, date_format, time_format, date_attrs, time_attrs)
for widget in self.widgets:
- widget.input_type = 'hidden'
+ widget.input_type = "hidden"
class SelectDateWidget(Widget):
@@ -938,14 +1014,15 @@ class SelectDateWidget(Widget):
This also serves as an example of a Widget that has more than one HTML
element and hence implements value_from_datadict.
"""
- none_value = ('', '---')
- month_field = '%s_month'
- day_field = '%s_day'
- year_field = '%s_year'
- template_name = 'django/forms/widgets/select_date.html'
- input_type = 'select'
+
+ none_value = ("", "---")
+ month_field = "%s_month"
+ day_field = "%s_day"
+ year_field = "%s_year"
+ template_name = "django/forms/widgets/select_date.html"
+ input_type = "select"
select_widget = Select
- date_re = _lazy_re_compile(r'(\d{4}|0)-(\d\d?)-(\d\d?)$')
+ date_re = _lazy_re_compile(r"(\d{4}|0)-(\d\d?)-(\d\d?)$")
def __init__(self, attrs=None, years=None, months=None, empty_label=None):
self.attrs = attrs or {}
@@ -966,14 +1043,14 @@ class SelectDateWidget(Widget):
# Optional string, list, or tuple to use as empty_label.
if isinstance(empty_label, (list, tuple)):
if not len(empty_label) == 3:
- raise ValueError('empty_label list/tuple must have 3 elements.')
+ raise ValueError("empty_label list/tuple must have 3 elements.")
- self.year_none_value = ('', empty_label[0])
- self.month_none_value = ('', empty_label[1])
- self.day_none_value = ('', empty_label[2])
+ self.year_none_value = ("", empty_label[0])
+ self.month_none_value = ("", empty_label[1])
+ self.day_none_value = ("", empty_label[2])
else:
if empty_label is not None:
- self.none_value = ('', empty_label)
+ self.none_value = ("", empty_label)
self.year_none_value = self.none_value
self.month_none_value = self.none_value
@@ -986,33 +1063,40 @@ class SelectDateWidget(Widget):
if not self.is_required:
year_choices.insert(0, self.year_none_value)
year_name = self.year_field % name
- date_context['year'] = self.select_widget(attrs, choices=year_choices).get_context(
+ 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},
+ value=context["widget"]["value"]["year"],
+ attrs={**context["widget"]["attrs"], "id": "id_%s" % year_name},
)
month_choices = list(self.months.items())
if not self.is_required:
month_choices.insert(0, self.month_none_value)
month_name = self.month_field % name
- date_context['month'] = self.select_widget(attrs, choices=month_choices).get_context(
+ 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},
+ value=context["widget"]["value"]["month"],
+ attrs={**context["widget"]["attrs"], "id": "id_%s" % month_name},
)
day_choices = [(i, i) for i in range(1, 32)]
if not self.is_required:
day_choices.insert(0, self.day_none_value)
day_name = self.day_field % name
- date_context['day'] = self.select_widget(attrs, choices=day_choices,).get_context(
+ 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},
+ value=context["widget"]["value"]["day"],
+ attrs={**context["widget"]["attrs"], "id": "id_%s" % day_name},
)
subwidgets = []
for field in self._parse_date_fmt():
- subwidgets.append(date_context[field]['widget'])
- context['widget']['subwidgets'] = subwidgets
+ subwidgets.append(date_context[field]["widget"])
+ context["widget"]["subwidgets"] = subwidgets
return context
def format_value(self, value):
@@ -1029,58 +1113,58 @@ class SelectDateWidget(Widget):
if match:
# Convert any zeros in the date to empty strings to match the
# empty option value.
- year, month, day = [int(val) or '' for val in match.groups()]
+ year, month, day = [int(val) or "" for val in match.groups()]
else:
- input_format = get_format('DATE_INPUT_FORMATS')[0]
+ input_format = get_format("DATE_INPUT_FORMATS")[0]
try:
d = datetime.datetime.strptime(value, input_format)
except ValueError:
pass
else:
year, month, day = d.year, d.month, d.day
- return {'year': year, 'month': month, 'day': day}
+ return {"year": year, "month": month, "day": day}
@staticmethod
def _parse_date_fmt():
- fmt = get_format('DATE_FORMAT')
+ fmt = get_format("DATE_FORMAT")
escaped = False
for char in fmt:
if escaped:
escaped = False
- elif char == '\\':
+ elif char == "\\":
escaped = True
- elif char in 'Yy':
- yield 'year'
- elif char in 'bEFMmNn':
- yield 'month'
- elif char in 'dj':
- yield 'day'
+ elif char in "Yy":
+ yield "year"
+ elif char in "bEFMmNn":
+ yield "month"
+ elif char in "dj":
+ yield "day"
def id_for_label(self, id_):
for first_select in self._parse_date_fmt():
- return '%s_%s' % (id_, first_select)
- return '%s_month' % id_
+ return "%s_%s" % (id_, first_select)
+ return "%s_month" % id_
def value_from_datadict(self, data, files, name):
y = data.get(self.year_field % name)
m = data.get(self.month_field % name)
d = data.get(self.day_field % name)
- if y == m == d == '':
+ if y == m == d == "":
return None
if y is not None and m is not None and d is not None:
- input_format = get_format('DATE_INPUT_FORMATS')[0]
+ input_format = get_format("DATE_INPUT_FORMATS")[0]
input_format = formats.sanitize_strftime_format(input_format)
try:
date_value = datetime.date(int(y), int(m), int(d))
except ValueError:
# Return pseudo-ISO dates with zeros for any unselected values,
# e.g. '2017-0-23'.
- return '%s-%s-%s' % (y or 0, m or 0, d or 0)
+ return "%s-%s-%s" % (y or 0, m or 0, d or 0)
return date_value.strftime(input_format)
return data.get(name)
def value_omitted_from_data(self, data, files, name):
return not any(
- ('{}_{}'.format(name, interval) in data)
- for interval in ('year', 'month', 'day')
+ ("{}_{}".format(name, interval) in data)
+ for interval in ("year", "month", "day")
)