docker setup
This commit is contained in:
@ -0,0 +1,11 @@
|
||||
"""
|
||||
Django validation and HTML form handling.
|
||||
"""
|
||||
|
||||
from django.core.exceptions import ValidationError # NOQA
|
||||
from django.forms.boundfield import * # NOQA
|
||||
from django.forms.fields import * # NOQA
|
||||
from django.forms.forms import * # NOQA
|
||||
from django.forms.formsets import * # NOQA
|
||||
from django.forms.models import * # NOQA
|
||||
from django.forms.widgets import * # NOQA
|
@ -0,0 +1,344 @@
|
||||
import re
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms.utils import pretty_name
|
||||
from django.forms.widgets import MultiWidget, Textarea, TextInput
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import format_html, html_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
__all__ = ("BoundField",)
|
||||
|
||||
|
||||
@html_safe
|
||||
class BoundField:
|
||||
"A Field plus data"
|
||||
|
||||
def __init__(self, form, field, name):
|
||||
self.form = form
|
||||
self.field = field
|
||||
self.name = name
|
||||
self.html_name = form.add_prefix(name)
|
||||
self.html_initial_name = form.add_initial_prefix(name)
|
||||
self.html_initial_id = form.add_initial_prefix(self.auto_id)
|
||||
if self.field.label is None:
|
||||
self.label = pretty_name(name)
|
||||
else:
|
||||
self.label = self.field.label
|
||||
self.help_text = field.help_text or ""
|
||||
|
||||
def __str__(self):
|
||||
"""Render this field as an HTML widget."""
|
||||
if self.field.show_hidden_initial:
|
||||
return self.as_widget() + self.as_hidden(only_initial=True)
|
||||
return self.as_widget()
|
||||
|
||||
@cached_property
|
||||
def subwidgets(self):
|
||||
"""
|
||||
Most widgets yield a single subwidget, but others like RadioSelect and
|
||||
CheckboxSelectMultiple produce one subwidget for each choice.
|
||||
|
||||
This property is cached so that only one database query occurs when
|
||||
rendering ModelChoiceFields.
|
||||
"""
|
||||
id_ = self.field.widget.attrs.get("id") or self.auto_id
|
||||
attrs = {"id": id_} if id_ else {}
|
||||
attrs = self.build_widget_attrs(attrs)
|
||||
return [
|
||||
BoundWidget(self.field.widget, widget, self.form.renderer)
|
||||
for widget in self.field.widget.subwidgets(
|
||||
self.html_name, self.value(), attrs=attrs
|
||||
)
|
||||
]
|
||||
|
||||
def __bool__(self):
|
||||
# BoundField evaluates to True even if it doesn't have subwidgets.
|
||||
return True
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.subwidgets)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.subwidgets)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
# Prevent unnecessary reevaluation when accessing BoundField's attrs
|
||||
# from templates.
|
||||
if not isinstance(idx, (int, slice)):
|
||||
raise TypeError(
|
||||
"BoundField indices must be integers or slices, not %s."
|
||||
% type(idx).__name__
|
||||
)
|
||||
return self.subwidgets[idx]
|
||||
|
||||
@property
|
||||
def errors(self):
|
||||
"""
|
||||
Return an ErrorList (empty if there are no errors) for this field.
|
||||
"""
|
||||
return self.form.errors.get(
|
||||
self.name, self.form.error_class(renderer=self.form.renderer)
|
||||
)
|
||||
|
||||
def as_widget(self, widget=None, attrs=None, only_initial=False):
|
||||
"""
|
||||
Render the field by rendering the passed widget, adding any HTML
|
||||
attributes passed as attrs. If a widget isn't specified, use the
|
||||
field's default widget.
|
||||
"""
|
||||
widget = widget or self.field.widget
|
||||
if self.field.localize:
|
||||
widget.is_localized = True
|
||||
attrs = attrs or {}
|
||||
attrs = self.build_widget_attrs(attrs, widget)
|
||||
if self.auto_id and "id" not in widget.attrs:
|
||||
attrs.setdefault(
|
||||
"id", self.html_initial_id if only_initial else self.auto_id
|
||||
)
|
||||
if only_initial and self.html_initial_name in self.form.data:
|
||||
# Propagate the hidden initial value.
|
||||
value = self.form._widget_data_value(
|
||||
self.field.hidden_widget(),
|
||||
self.html_initial_name,
|
||||
)
|
||||
else:
|
||||
value = self.value()
|
||||
return widget.render(
|
||||
name=self.html_initial_name if only_initial else self.html_name,
|
||||
value=value,
|
||||
attrs=attrs,
|
||||
renderer=self.form.renderer,
|
||||
)
|
||||
|
||||
def as_text(self, attrs=None, **kwargs):
|
||||
"""
|
||||
Return a string of HTML for representing this as an <input type="text">.
|
||||
"""
|
||||
return self.as_widget(TextInput(), attrs, **kwargs)
|
||||
|
||||
def as_textarea(self, attrs=None, **kwargs):
|
||||
"""Return a string of HTML for representing this as a <textarea>."""
|
||||
return self.as_widget(Textarea(), attrs, **kwargs)
|
||||
|
||||
def as_hidden(self, attrs=None, **kwargs):
|
||||
"""
|
||||
Return a string of HTML for representing this as an <input type="hidden">.
|
||||
"""
|
||||
return self.as_widget(self.field.hidden_widget(), attrs, **kwargs)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""
|
||||
Return the data for this BoundField, or None if it wasn't given.
|
||||
"""
|
||||
return self.form._widget_data_value(self.field.widget, self.html_name)
|
||||
|
||||
def value(self):
|
||||
"""
|
||||
Return the value for this BoundField, using the initial value if
|
||||
the form is not bound or the data otherwise.
|
||||
"""
|
||||
data = self.initial
|
||||
if self.form.is_bound:
|
||||
data = self.field.bound_data(self.data, data)
|
||||
return self.field.prepare_value(data)
|
||||
|
||||
def _has_changed(self):
|
||||
field = self.field
|
||||
if field.show_hidden_initial:
|
||||
hidden_widget = field.hidden_widget()
|
||||
initial_value = self.form._widget_data_value(
|
||||
hidden_widget,
|
||||
self.html_initial_name,
|
||||
)
|
||||
try:
|
||||
initial_value = field.to_python(initial_value)
|
||||
except ValidationError:
|
||||
# Always assume data has changed if validation fails.
|
||||
return True
|
||||
else:
|
||||
initial_value = self.initial
|
||||
return field.has_changed(initial_value, self.data)
|
||||
|
||||
def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
|
||||
"""
|
||||
Wrap the given contents in a <label>, if the field has an ID attribute.
|
||||
contents should be mark_safe'd to avoid HTML escaping. If contents
|
||||
aren't given, use the field's HTML-escaped label.
|
||||
|
||||
If attrs are given, use them as HTML attributes on the <label> tag.
|
||||
|
||||
label_suffix overrides the form's label_suffix.
|
||||
"""
|
||||
contents = contents or self.label
|
||||
if label_suffix is None:
|
||||
label_suffix = (
|
||||
self.field.label_suffix
|
||||
if self.field.label_suffix is not None
|
||||
else self.form.label_suffix
|
||||
)
|
||||
# Only add the suffix if the label does not end in punctuation.
|
||||
# Translators: If found as last label character, these punctuation
|
||||
# characters will prevent the default label_suffix to be appended to the label
|
||||
if label_suffix and contents and contents[-1] not in _(":?.!"):
|
||||
contents = format_html("{}{}", contents, label_suffix)
|
||||
widget = self.field.widget
|
||||
id_ = widget.attrs.get("id") or self.auto_id
|
||||
if id_:
|
||||
id_for_label = widget.id_for_label(id_)
|
||||
if id_for_label:
|
||||
attrs = {**(attrs or {}), "for": id_for_label}
|
||||
if self.field.required and hasattr(self.form, "required_css_class"):
|
||||
attrs = attrs or {}
|
||||
if "class" in attrs:
|
||||
attrs["class"] += " " + self.form.required_css_class
|
||||
else:
|
||||
attrs["class"] = self.form.required_css_class
|
||||
context = {
|
||||
"field": self,
|
||||
"label": contents,
|
||||
"attrs": attrs,
|
||||
"use_tag": bool(id_),
|
||||
"tag": tag or "label",
|
||||
}
|
||||
return self.form.render(self.form.template_name_label, context)
|
||||
|
||||
def legend_tag(self, contents=None, attrs=None, label_suffix=None):
|
||||
"""
|
||||
Wrap the given contents in a <legend>, if the field has an ID
|
||||
attribute. Contents should be mark_safe'd to avoid HTML escaping. If
|
||||
contents aren't given, use the field's HTML-escaped label.
|
||||
|
||||
If attrs are given, use them as HTML attributes on the <legend> tag.
|
||||
|
||||
label_suffix overrides the form's label_suffix.
|
||||
"""
|
||||
return self.label_tag(contents, attrs, label_suffix, tag="legend")
|
||||
|
||||
def css_classes(self, extra_classes=None):
|
||||
"""
|
||||
Return a string of space-separated CSS classes for this field.
|
||||
"""
|
||||
if hasattr(extra_classes, "split"):
|
||||
extra_classes = extra_classes.split()
|
||||
extra_classes = set(extra_classes or [])
|
||||
if self.errors and hasattr(self.form, "error_css_class"):
|
||||
extra_classes.add(self.form.error_css_class)
|
||||
if self.field.required and hasattr(self.form, "required_css_class"):
|
||||
extra_classes.add(self.form.required_css_class)
|
||||
return " ".join(extra_classes)
|
||||
|
||||
@property
|
||||
def is_hidden(self):
|
||||
"""Return True if this BoundField's widget is hidden."""
|
||||
return self.field.widget.is_hidden
|
||||
|
||||
@property
|
||||
def auto_id(self):
|
||||
"""
|
||||
Calculate and return the ID attribute for this BoundField, if the
|
||||
associated Form has specified auto_id. Return an empty string otherwise.
|
||||
"""
|
||||
auto_id = self.form.auto_id # Boolean or string
|
||||
if auto_id and "%s" in str(auto_id):
|
||||
return auto_id % self.html_name
|
||||
elif auto_id:
|
||||
return self.html_name
|
||||
return ""
|
||||
|
||||
@property
|
||||
def id_for_label(self):
|
||||
"""
|
||||
Wrapper around the field widget's `id_for_label` method.
|
||||
Useful, for example, for focusing on this field regardless of whether
|
||||
it has a single widget or a MultiWidget.
|
||||
"""
|
||||
widget = self.field.widget
|
||||
id_ = widget.attrs.get("id") or self.auto_id
|
||||
return widget.id_for_label(id_)
|
||||
|
||||
@cached_property
|
||||
def initial(self):
|
||||
return self.form.get_initial_for_field(self.field, self.name)
|
||||
|
||||
def build_widget_attrs(self, attrs, widget=None):
|
||||
widget = widget or self.field.widget
|
||||
attrs = dict(attrs) # Copy attrs to avoid modifying the argument.
|
||||
if (
|
||||
widget.use_required_attribute(self.initial)
|
||||
and self.field.required
|
||||
and self.form.use_required_attribute
|
||||
):
|
||||
# MultiValueField has require_all_fields: if False, fall back
|
||||
# on subfields.
|
||||
if (
|
||||
hasattr(self.field, "require_all_fields")
|
||||
and not self.field.require_all_fields
|
||||
and isinstance(self.field.widget, MultiWidget)
|
||||
):
|
||||
for subfield, subwidget in zip(self.field.fields, widget.widgets):
|
||||
subwidget.attrs["required"] = (
|
||||
subwidget.use_required_attribute(self.initial)
|
||||
and subfield.required
|
||||
)
|
||||
else:
|
||||
attrs["required"] = True
|
||||
if self.field.disabled:
|
||||
attrs["disabled"] = True
|
||||
return attrs
|
||||
|
||||
@property
|
||||
def widget_type(self):
|
||||
return re.sub(
|
||||
r"widget$|input$", "", self.field.widget.__class__.__name__.lower()
|
||||
)
|
||||
|
||||
@property
|
||||
def use_fieldset(self):
|
||||
"""
|
||||
Return the value of this BoundField widget's use_fieldset attribute.
|
||||
"""
|
||||
return self.field.widget.use_fieldset
|
||||
|
||||
|
||||
@html_safe
|
||||
class BoundWidget:
|
||||
"""
|
||||
A container class used for iterating over widgets. This is useful for
|
||||
widgets that have choices. For example, the following can be used in a
|
||||
template:
|
||||
|
||||
{% for radio in myform.beatles %}
|
||||
<label for="{{ radio.id_for_label }}">
|
||||
{{ radio.choice_label }}
|
||||
<span class="radio">{{ radio.tag }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
"""
|
||||
|
||||
def __init__(self, parent_widget, data, renderer):
|
||||
self.parent_widget = parent_widget
|
||||
self.data = data
|
||||
self.renderer = renderer
|
||||
|
||||
def __str__(self):
|
||||
return self.tag(wrap_label=True)
|
||||
|
||||
def tag(self, wrap_label=False):
|
||||
context = {"widget": {**self.data, "wrap_label": wrap_label}}
|
||||
return self.parent_widget._render(self.template_name, context, self.renderer)
|
||||
|
||||
@property
|
||||
def template_name(self):
|
||||
if "template_name" in self.data:
|
||||
return self.data["template_name"]
|
||||
return self.parent_widget.template_name
|
||||
|
||||
@property
|
||||
def id_for_label(self):
|
||||
return self.data["attrs"].get("id")
|
||||
|
||||
@property
|
||||
def choice_label(self):
|
||||
return self.data["label"]
|
1391
srcs/.venv/lib/python3.11/site-packages/django/forms/fields.py
Normal file
1391
srcs/.venv/lib/python3.11/site-packages/django/forms/fields.py
Normal file
File diff suppressed because it is too large
Load Diff
539
srcs/.venv/lib/python3.11/site-packages/django/forms/forms.py
Normal file
539
srcs/.venv/lib/python3.11/site-packages/django/forms/forms.py
Normal file
@ -0,0 +1,539 @@
|
||||
"""
|
||||
Form classes
|
||||
"""
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import warnings
|
||||
|
||||
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
|
||||
from django.forms.fields import Field, FileField
|
||||
from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin
|
||||
from django.forms.widgets import Media, MediaDefiningClass
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.deprecation import RemovedInDjango50Warning
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.safestring import SafeString, mark_safe
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from .renderers import get_default_renderer
|
||||
|
||||
__all__ = ("BaseForm", "Form")
|
||||
|
||||
|
||||
class DeclarativeFieldsMetaclass(MediaDefiningClass):
|
||||
"""Collect Fields declared on the base classes."""
|
||||
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
# Collect fields from current class and remove them from attrs.
|
||||
attrs["declared_fields"] = {
|
||||
key: attrs.pop(key)
|
||||
for key, value in list(attrs.items())
|
||||
if isinstance(value, Field)
|
||||
}
|
||||
|
||||
new_class = super().__new__(mcs, name, bases, attrs)
|
||||
|
||||
# Walk through the MRO.
|
||||
declared_fields = {}
|
||||
for base in reversed(new_class.__mro__):
|
||||
# Collect fields from base class.
|
||||
if hasattr(base, "declared_fields"):
|
||||
declared_fields.update(base.declared_fields)
|
||||
|
||||
# Field shadowing.
|
||||
for attr, value in base.__dict__.items():
|
||||
if value is None and attr in declared_fields:
|
||||
declared_fields.pop(attr)
|
||||
|
||||
new_class.base_fields = declared_fields
|
||||
new_class.declared_fields = declared_fields
|
||||
|
||||
return new_class
|
||||
|
||||
|
||||
class BaseForm(RenderableFormMixin):
|
||||
"""
|
||||
The main implementation of all the Form logic. Note that this class is
|
||||
different than Form. See the comments by the Form class for more info. Any
|
||||
improvements to the form API should be made to this class, not to the Form
|
||||
class.
|
||||
"""
|
||||
|
||||
default_renderer = None
|
||||
field_order = None
|
||||
prefix = None
|
||||
use_required_attribute = True
|
||||
|
||||
template_name_div = "django/forms/div.html"
|
||||
template_name_p = "django/forms/p.html"
|
||||
template_name_table = "django/forms/table.html"
|
||||
template_name_ul = "django/forms/ul.html"
|
||||
template_name_label = "django/forms/label.html"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data=None,
|
||||
files=None,
|
||||
auto_id="id_%s",
|
||||
prefix=None,
|
||||
initial=None,
|
||||
error_class=ErrorList,
|
||||
label_suffix=None,
|
||||
empty_permitted=False,
|
||||
field_order=None,
|
||||
use_required_attribute=None,
|
||||
renderer=None,
|
||||
):
|
||||
self.is_bound = data is not None or files is not None
|
||||
self.data = MultiValueDict() if data is None else data
|
||||
self.files = MultiValueDict() if files is None else files
|
||||
self.auto_id = auto_id
|
||||
if prefix is not None:
|
||||
self.prefix = prefix
|
||||
self.initial = initial or {}
|
||||
self.error_class = error_class
|
||||
# Translators: This is the default suffix added to form field labels
|
||||
self.label_suffix = label_suffix if label_suffix is not None else _(":")
|
||||
self.empty_permitted = empty_permitted
|
||||
self._errors = None # Stores the errors after clean() has been called.
|
||||
|
||||
# The base_fields class attribute is the *class-wide* definition of
|
||||
# fields. Because a particular *instance* of the class might want to
|
||||
# alter self.fields, we create self.fields here by copying base_fields.
|
||||
# Instances should always modify self.fields; they should not modify
|
||||
# self.base_fields.
|
||||
self.fields = copy.deepcopy(self.base_fields)
|
||||
self._bound_fields_cache = {}
|
||||
self.order_fields(self.field_order if field_order is None else field_order)
|
||||
|
||||
if use_required_attribute is not None:
|
||||
self.use_required_attribute = use_required_attribute
|
||||
|
||||
if self.empty_permitted and self.use_required_attribute:
|
||||
raise ValueError(
|
||||
"The empty_permitted and use_required_attribute arguments may "
|
||||
"not both be True."
|
||||
)
|
||||
|
||||
# Initialize form renderer. Use a global default if not specified
|
||||
# either as an argument or as self.default_renderer.
|
||||
if renderer is None:
|
||||
if self.default_renderer is None:
|
||||
renderer = get_default_renderer()
|
||||
else:
|
||||
renderer = self.default_renderer
|
||||
if isinstance(self.default_renderer, type):
|
||||
renderer = renderer()
|
||||
self.renderer = renderer
|
||||
|
||||
def order_fields(self, field_order):
|
||||
"""
|
||||
Rearrange the fields according to field_order.
|
||||
|
||||
field_order is a list of field names specifying the order. Append fields
|
||||
not included in the list in the default order for backward compatibility
|
||||
with subclasses not overriding field_order. If field_order is None,
|
||||
keep all fields in the order defined in the class. Ignore unknown
|
||||
fields in field_order to allow disabling fields in form subclasses
|
||||
without redefining ordering.
|
||||
"""
|
||||
if field_order is None:
|
||||
return
|
||||
fields = {}
|
||||
for key in field_order:
|
||||
try:
|
||||
fields[key] = self.fields.pop(key)
|
||||
except KeyError: # ignore unknown fields
|
||||
pass
|
||||
fields.update(self.fields) # add remaining fields in original order
|
||||
self.fields = fields
|
||||
|
||||
def __repr__(self):
|
||||
if self._errors is None:
|
||||
is_valid = "Unknown"
|
||||
else:
|
||||
is_valid = self.is_bound and not self._errors
|
||||
return "<%(cls)s bound=%(bound)s, valid=%(valid)s, fields=(%(fields)s)>" % {
|
||||
"cls": self.__class__.__name__,
|
||||
"bound": self.is_bound,
|
||||
"valid": is_valid,
|
||||
"fields": ";".join(self.fields),
|
||||
}
|
||||
|
||||
def _bound_items(self):
|
||||
"""Yield (name, bf) pairs, where bf is a BoundField object."""
|
||||
for name in self.fields:
|
||||
yield name, self[name]
|
||||
|
||||
def __iter__(self):
|
||||
"""Yield the form's fields as BoundField objects."""
|
||||
for name in self.fields:
|
||||
yield self[name]
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""Return a BoundField with the given name."""
|
||||
try:
|
||||
field = self.fields[name]
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
"Key '%s' not found in '%s'. Choices are: %s."
|
||||
% (
|
||||
name,
|
||||
self.__class__.__name__,
|
||||
", ".join(sorted(self.fields)),
|
||||
)
|
||||
)
|
||||
if name not in self._bound_fields_cache:
|
||||
self._bound_fields_cache[name] = field.get_bound_field(self, name)
|
||||
return self._bound_fields_cache[name]
|
||||
|
||||
@property
|
||||
def errors(self):
|
||||
"""Return an ErrorDict for the data provided for the form."""
|
||||
if self._errors is None:
|
||||
self.full_clean()
|
||||
return self._errors
|
||||
|
||||
def is_valid(self):
|
||||
"""Return True if the form has no errors, or False otherwise."""
|
||||
return self.is_bound and not self.errors
|
||||
|
||||
def add_prefix(self, field_name):
|
||||
"""
|
||||
Return the field name with a prefix appended, if this Form has a
|
||||
prefix set.
|
||||
|
||||
Subclasses may wish to override.
|
||||
"""
|
||||
return "%s-%s" % (self.prefix, field_name) if self.prefix else field_name
|
||||
|
||||
def add_initial_prefix(self, field_name):
|
||||
"""Add an 'initial' prefix for checking dynamic initial values."""
|
||||
return "initial-%s" % self.add_prefix(field_name)
|
||||
|
||||
def _widget_data_value(self, widget, html_name):
|
||||
# value_from_datadict() gets the data from the data dictionaries.
|
||||
# Each widget type knows how to retrieve its own data, because some
|
||||
# widgets split data over several HTML fields.
|
||||
return widget.value_from_datadict(self.data, self.files, html_name)
|
||||
|
||||
def _html_output(
|
||||
self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row
|
||||
):
|
||||
"Output HTML. Used by as_table(), as_ul(), as_p()."
|
||||
warnings.warn(
|
||||
"django.forms.BaseForm._html_output() is deprecated. "
|
||||
"Please use .render() and .get_context() instead.",
|
||||
RemovedInDjango50Warning,
|
||||
stacklevel=2,
|
||||
)
|
||||
# Errors that should be displayed above all fields.
|
||||
top_errors = self.non_field_errors().copy()
|
||||
output, hidden_fields = [], []
|
||||
|
||||
for name, bf in self._bound_items():
|
||||
field = bf.field
|
||||
html_class_attr = ""
|
||||
bf_errors = self.error_class(bf.errors)
|
||||
if bf.is_hidden:
|
||||
if bf_errors:
|
||||
top_errors.extend(
|
||||
[
|
||||
_("(Hidden field %(name)s) %(error)s")
|
||||
% {"name": name, "error": str(e)}
|
||||
for e in bf_errors
|
||||
]
|
||||
)
|
||||
hidden_fields.append(str(bf))
|
||||
else:
|
||||
# Create a 'class="..."' attribute if the row should have any
|
||||
# CSS classes applied.
|
||||
css_classes = bf.css_classes()
|
||||
if css_classes:
|
||||
html_class_attr = ' class="%s"' % css_classes
|
||||
|
||||
if errors_on_separate_row and bf_errors:
|
||||
output.append(error_row % str(bf_errors))
|
||||
|
||||
if bf.label:
|
||||
label = conditional_escape(bf.label)
|
||||
label = bf.label_tag(label) or ""
|
||||
else:
|
||||
label = ""
|
||||
|
||||
if field.help_text:
|
||||
help_text = help_text_html % field.help_text
|
||||
else:
|
||||
help_text = ""
|
||||
|
||||
output.append(
|
||||
normal_row
|
||||
% {
|
||||
"errors": bf_errors,
|
||||
"label": label,
|
||||
"field": bf,
|
||||
"help_text": help_text,
|
||||
"html_class_attr": html_class_attr,
|
||||
"css_classes": css_classes,
|
||||
"field_name": bf.html_name,
|
||||
}
|
||||
)
|
||||
|
||||
if top_errors:
|
||||
output.insert(0, error_row % top_errors)
|
||||
|
||||
if hidden_fields: # Insert any hidden fields in the last row.
|
||||
str_hidden = "".join(hidden_fields)
|
||||
if output:
|
||||
last_row = output[-1]
|
||||
# Chop off the trailing row_ender (e.g. '</td></tr>') and
|
||||
# insert the hidden fields.
|
||||
if not last_row.endswith(row_ender):
|
||||
# This can happen in the as_p() case (and possibly others
|
||||
# that users write): if there are only top errors, we may
|
||||
# not be able to conscript the last row for our purposes,
|
||||
# so insert a new, empty row.
|
||||
last_row = normal_row % {
|
||||
"errors": "",
|
||||
"label": "",
|
||||
"field": "",
|
||||
"help_text": "",
|
||||
"html_class_attr": html_class_attr,
|
||||
"css_classes": "",
|
||||
"field_name": "",
|
||||
}
|
||||
output.append(last_row)
|
||||
output[-1] = last_row[: -len(row_ender)] + str_hidden + row_ender
|
||||
else:
|
||||
# If there aren't any rows in the output, just append the
|
||||
# hidden fields.
|
||||
output.append(str_hidden)
|
||||
return mark_safe("\n".join(output))
|
||||
|
||||
@property
|
||||
def template_name(self):
|
||||
return self.renderer.form_template_name
|
||||
|
||||
def get_context(self):
|
||||
fields = []
|
||||
hidden_fields = []
|
||||
top_errors = self.non_field_errors().copy()
|
||||
for name, bf in self._bound_items():
|
||||
bf_errors = self.error_class(bf.errors, renderer=self.renderer)
|
||||
if bf.is_hidden:
|
||||
if bf_errors:
|
||||
top_errors += [
|
||||
_("(Hidden field %(name)s) %(error)s")
|
||||
% {"name": name, "error": str(e)}
|
||||
for e in bf_errors
|
||||
]
|
||||
hidden_fields.append(bf)
|
||||
else:
|
||||
errors_str = str(bf_errors)
|
||||
# RemovedInDjango50Warning.
|
||||
if not isinstance(errors_str, SafeString):
|
||||
warnings.warn(
|
||||
f"Returning a plain string from "
|
||||
f"{self.error_class.__name__} is deprecated. Please "
|
||||
f"customize via the template system instead.",
|
||||
RemovedInDjango50Warning,
|
||||
)
|
||||
errors_str = mark_safe(errors_str)
|
||||
fields.append((bf, errors_str))
|
||||
return {
|
||||
"form": self,
|
||||
"fields": fields,
|
||||
"hidden_fields": hidden_fields,
|
||||
"errors": top_errors,
|
||||
}
|
||||
|
||||
def non_field_errors(self):
|
||||
"""
|
||||
Return an ErrorList of errors that aren't associated with a particular
|
||||
field -- i.e., from Form.clean(). Return an empty ErrorList if there
|
||||
are none.
|
||||
"""
|
||||
return self.errors.get(
|
||||
NON_FIELD_ERRORS,
|
||||
self.error_class(error_class="nonfield", renderer=self.renderer),
|
||||
)
|
||||
|
||||
def add_error(self, field, error):
|
||||
"""
|
||||
Update the content of `self._errors`.
|
||||
|
||||
The `field` argument is the name of the field to which the errors
|
||||
should be added. If it's None, treat the errors as NON_FIELD_ERRORS.
|
||||
|
||||
The `error` argument can be a single error, a list of errors, or a
|
||||
dictionary that maps field names to lists of errors. An "error" can be
|
||||
either a simple string or an instance of ValidationError with its
|
||||
message attribute set and a "list or dictionary" can be an actual
|
||||
`list` or `dict` or an instance of ValidationError with its
|
||||
`error_list` or `error_dict` attribute set.
|
||||
|
||||
If `error` is a dictionary, the `field` argument *must* be None and
|
||||
errors will be added to the fields that correspond to the keys of the
|
||||
dictionary.
|
||||
"""
|
||||
if not isinstance(error, ValidationError):
|
||||
# Normalize to ValidationError and let its constructor
|
||||
# do the hard work of making sense of the input.
|
||||
error = ValidationError(error)
|
||||
|
||||
if hasattr(error, "error_dict"):
|
||||
if field is not None:
|
||||
raise TypeError(
|
||||
"The argument `field` must be `None` when the `error` "
|
||||
"argument contains errors for multiple fields."
|
||||
)
|
||||
else:
|
||||
error = error.error_dict
|
||||
else:
|
||||
error = {field or NON_FIELD_ERRORS: error.error_list}
|
||||
|
||||
for field, error_list in error.items():
|
||||
if field not in self.errors:
|
||||
if field != NON_FIELD_ERRORS and field not in self.fields:
|
||||
raise ValueError(
|
||||
"'%s' has no field named '%s'."
|
||||
% (self.__class__.__name__, field)
|
||||
)
|
||||
if field == NON_FIELD_ERRORS:
|
||||
self._errors[field] = self.error_class(
|
||||
error_class="nonfield", renderer=self.renderer
|
||||
)
|
||||
else:
|
||||
self._errors[field] = self.error_class(renderer=self.renderer)
|
||||
self._errors[field].extend(error_list)
|
||||
if field in self.cleaned_data:
|
||||
del self.cleaned_data[field]
|
||||
|
||||
def has_error(self, field, code=None):
|
||||
return field in self.errors and (
|
||||
code is None
|
||||
or any(error.code == code for error in self.errors.as_data()[field])
|
||||
)
|
||||
|
||||
def full_clean(self):
|
||||
"""
|
||||
Clean all of self.data and populate self._errors and self.cleaned_data.
|
||||
"""
|
||||
self._errors = ErrorDict()
|
||||
if not self.is_bound: # Stop further processing.
|
||||
return
|
||||
self.cleaned_data = {}
|
||||
# If the form is permitted to be empty, and none of the form data has
|
||||
# changed from the initial data, short circuit any validation.
|
||||
if self.empty_permitted and not self.has_changed():
|
||||
return
|
||||
|
||||
self._clean_fields()
|
||||
self._clean_form()
|
||||
self._post_clean()
|
||||
|
||||
def _clean_fields(self):
|
||||
for name, bf in self._bound_items():
|
||||
field = bf.field
|
||||
value = bf.initial if field.disabled else bf.data
|
||||
try:
|
||||
if isinstance(field, FileField):
|
||||
value = field.clean(value, bf.initial)
|
||||
else:
|
||||
value = field.clean(value)
|
||||
self.cleaned_data[name] = value
|
||||
if hasattr(self, "clean_%s" % name):
|
||||
value = getattr(self, "clean_%s" % name)()
|
||||
self.cleaned_data[name] = value
|
||||
except ValidationError as e:
|
||||
self.add_error(name, e)
|
||||
|
||||
def _clean_form(self):
|
||||
try:
|
||||
cleaned_data = self.clean()
|
||||
except ValidationError as e:
|
||||
self.add_error(None, e)
|
||||
else:
|
||||
if cleaned_data is not None:
|
||||
self.cleaned_data = cleaned_data
|
||||
|
||||
def _post_clean(self):
|
||||
"""
|
||||
An internal hook for performing additional cleaning after form cleaning
|
||||
is complete. Used for model validation in model forms.
|
||||
"""
|
||||
pass
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Hook for doing any extra form-wide cleaning after Field.clean() has been
|
||||
called on every field. Any ValidationError raised by this method will
|
||||
not be associated with a particular field; it will have a special-case
|
||||
association with the field named '__all__'.
|
||||
"""
|
||||
return self.cleaned_data
|
||||
|
||||
def has_changed(self):
|
||||
"""Return True if data differs from initial."""
|
||||
return bool(self.changed_data)
|
||||
|
||||
@cached_property
|
||||
def changed_data(self):
|
||||
return [name for name, bf in self._bound_items() if bf._has_changed()]
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
"""Return all media required to render the widgets on this form."""
|
||||
media = Media()
|
||||
for field in self.fields.values():
|
||||
media += field.widget.media
|
||||
return media
|
||||
|
||||
def is_multipart(self):
|
||||
"""
|
||||
Return True if the form needs to be multipart-encoded, i.e. it has
|
||||
FileInput, or False otherwise.
|
||||
"""
|
||||
return any(field.widget.needs_multipart_form for field in self.fields.values())
|
||||
|
||||
def hidden_fields(self):
|
||||
"""
|
||||
Return a list of all the BoundField objects that are hidden fields.
|
||||
Useful for manual form layout in templates.
|
||||
"""
|
||||
return [field for field in self if field.is_hidden]
|
||||
|
||||
def visible_fields(self):
|
||||
"""
|
||||
Return a list of BoundField objects that aren't hidden fields.
|
||||
The opposite of the hidden_fields() method.
|
||||
"""
|
||||
return [field for field in self if not field.is_hidden]
|
||||
|
||||
def get_initial_for_field(self, field, field_name):
|
||||
"""
|
||||
Return initial data for field on form. Use initial data from the form
|
||||
or the field, in that order. Evaluate callable values.
|
||||
"""
|
||||
value = self.initial.get(field_name, field.initial)
|
||||
if callable(value):
|
||||
value = value()
|
||||
# If this is an auto-generated default date, nix the microseconds
|
||||
# for standardized handling. See #22502.
|
||||
if (
|
||||
isinstance(value, (datetime.datetime, datetime.time))
|
||||
and not field.widget.supports_microseconds
|
||||
):
|
||||
value = value.replace(microsecond=0)
|
||||
return value
|
||||
|
||||
|
||||
class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass):
|
||||
"A collection of Fields, plus their associated data."
|
||||
# This is a separate class from BaseForm in order to abstract the way
|
||||
# self.fields is specified. This class (Form) is the one that does the
|
||||
# fancy metaclass stuff purely for the semantic sugar -- it allows one
|
||||
# to define a form using declarative syntax.
|
||||
# BaseForm itself has no way of designating self.fields.
|
579
srcs/.venv/lib/python3.11/site-packages/django/forms/formsets.py
Normal file
579
srcs/.venv/lib/python3.11/site-packages/django/forms/formsets.py
Normal file
@ -0,0 +1,579 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms import Form
|
||||
from django.forms.fields import BooleanField, IntegerField
|
||||
from django.forms.renderers import get_default_renderer
|
||||
from django.forms.utils import ErrorList, RenderableFormMixin
|
||||
from django.forms.widgets import CheckboxInput, HiddenInput, NumberInput
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
__all__ = ("BaseFormSet", "formset_factory", "all_valid")
|
||||
|
||||
# special field names
|
||||
TOTAL_FORM_COUNT = "TOTAL_FORMS"
|
||||
INITIAL_FORM_COUNT = "INITIAL_FORMS"
|
||||
MIN_NUM_FORM_COUNT = "MIN_NUM_FORMS"
|
||||
MAX_NUM_FORM_COUNT = "MAX_NUM_FORMS"
|
||||
ORDERING_FIELD_NAME = "ORDER"
|
||||
DELETION_FIELD_NAME = "DELETE"
|
||||
|
||||
# default minimum number of forms in a formset
|
||||
DEFAULT_MIN_NUM = 0
|
||||
|
||||
# default maximum number of forms in a formset, to prevent memory exhaustion
|
||||
DEFAULT_MAX_NUM = 1000
|
||||
|
||||
|
||||
class ManagementForm(Form):
|
||||
"""
|
||||
Keep track of how many form instances are displayed on the page. If adding
|
||||
new forms via JavaScript, you should increment the count field of this form
|
||||
as well.
|
||||
"""
|
||||
|
||||
template_name = "django/forms/div.html" # RemovedInDjango50Warning.
|
||||
|
||||
TOTAL_FORMS = IntegerField(widget=HiddenInput)
|
||||
INITIAL_FORMS = IntegerField(widget=HiddenInput)
|
||||
# MIN_NUM_FORM_COUNT and MAX_NUM_FORM_COUNT are output with the rest of the
|
||||
# management form, but only for the convenience of client-side code. The
|
||||
# POST value of them returned from the client is not checked.
|
||||
MIN_NUM_FORMS = IntegerField(required=False, widget=HiddenInput)
|
||||
MAX_NUM_FORMS = IntegerField(required=False, widget=HiddenInput)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
# When the management form is invalid, we don't know how many forms
|
||||
# were submitted.
|
||||
cleaned_data.setdefault(TOTAL_FORM_COUNT, 0)
|
||||
cleaned_data.setdefault(INITIAL_FORM_COUNT, 0)
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class BaseFormSet(RenderableFormMixin):
|
||||
"""
|
||||
A collection of instances of the same Form class.
|
||||
"""
|
||||
|
||||
deletion_widget = CheckboxInput
|
||||
ordering_widget = NumberInput
|
||||
default_error_messages = {
|
||||
"missing_management_form": _(
|
||||
"ManagementForm data is missing or has been tampered with. Missing fields: "
|
||||
"%(field_names)s. You may need to file a bug report if the issue persists."
|
||||
),
|
||||
"too_many_forms": ngettext_lazy(
|
||||
"Please submit at most %(num)d form.",
|
||||
"Please submit at most %(num)d forms.",
|
||||
"num",
|
||||
),
|
||||
"too_few_forms": ngettext_lazy(
|
||||
"Please submit at least %(num)d form.",
|
||||
"Please submit at least %(num)d forms.",
|
||||
"num",
|
||||
),
|
||||
}
|
||||
|
||||
template_name_div = "django/forms/formsets/div.html"
|
||||
template_name_p = "django/forms/formsets/p.html"
|
||||
template_name_table = "django/forms/formsets/table.html"
|
||||
template_name_ul = "django/forms/formsets/ul.html"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data=None,
|
||||
files=None,
|
||||
auto_id="id_%s",
|
||||
prefix=None,
|
||||
initial=None,
|
||||
error_class=ErrorList,
|
||||
form_kwargs=None,
|
||||
error_messages=None,
|
||||
):
|
||||
self.is_bound = data is not None or files is not None
|
||||
self.prefix = prefix or self.get_default_prefix()
|
||||
self.auto_id = auto_id
|
||||
self.data = data or {}
|
||||
self.files = files or {}
|
||||
self.initial = initial
|
||||
self.form_kwargs = form_kwargs or {}
|
||||
self.error_class = error_class
|
||||
self._errors = None
|
||||
self._non_form_errors = None
|
||||
|
||||
messages = {}
|
||||
for cls in reversed(type(self).__mro__):
|
||||
messages.update(getattr(cls, "default_error_messages", {}))
|
||||
if error_messages is not None:
|
||||
messages.update(error_messages)
|
||||
self.error_messages = messages
|
||||
|
||||
def __iter__(self):
|
||||
"""Yield the forms in the order they should be rendered."""
|
||||
return iter(self.forms)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Return the form at the given index, based on the rendering order."""
|
||||
return self.forms[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.forms)
|
||||
|
||||
def __bool__(self):
|
||||
"""
|
||||
Return True since all formsets have a management form which is not
|
||||
included in the length.
|
||||
"""
|
||||
return True
|
||||
|
||||
def __repr__(self):
|
||||
if self._errors is None:
|
||||
is_valid = "Unknown"
|
||||
else:
|
||||
is_valid = (
|
||||
self.is_bound
|
||||
and not self._non_form_errors
|
||||
and not any(form_errors for form_errors in self._errors)
|
||||
)
|
||||
return "<%s: bound=%s valid=%s total_forms=%s>" % (
|
||||
self.__class__.__qualname__,
|
||||
self.is_bound,
|
||||
is_valid,
|
||||
self.total_form_count(),
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def management_form(self):
|
||||
"""Return the ManagementForm instance for this FormSet."""
|
||||
if self.is_bound:
|
||||
form = ManagementForm(
|
||||
self.data,
|
||||
auto_id=self.auto_id,
|
||||
prefix=self.prefix,
|
||||
renderer=self.renderer,
|
||||
)
|
||||
form.full_clean()
|
||||
else:
|
||||
form = ManagementForm(
|
||||
auto_id=self.auto_id,
|
||||
prefix=self.prefix,
|
||||
initial={
|
||||
TOTAL_FORM_COUNT: self.total_form_count(),
|
||||
INITIAL_FORM_COUNT: self.initial_form_count(),
|
||||
MIN_NUM_FORM_COUNT: self.min_num,
|
||||
MAX_NUM_FORM_COUNT: self.max_num,
|
||||
},
|
||||
renderer=self.renderer,
|
||||
)
|
||||
return form
|
||||
|
||||
def total_form_count(self):
|
||||
"""Return the total number of forms in this FormSet."""
|
||||
if self.is_bound:
|
||||
# return absolute_max if it is lower than the actual total form
|
||||
# count in the data; this is DoS protection to prevent clients
|
||||
# from forcing the server to instantiate arbitrary numbers of
|
||||
# forms
|
||||
return min(
|
||||
self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max
|
||||
)
|
||||
else:
|
||||
initial_forms = self.initial_form_count()
|
||||
total_forms = max(initial_forms, self.min_num) + self.extra
|
||||
# Allow all existing related objects/inlines to be displayed,
|
||||
# but don't allow extra beyond max_num.
|
||||
if initial_forms > self.max_num >= 0:
|
||||
total_forms = initial_forms
|
||||
elif total_forms > self.max_num >= 0:
|
||||
total_forms = self.max_num
|
||||
return total_forms
|
||||
|
||||
def initial_form_count(self):
|
||||
"""Return the number of forms that are required in this FormSet."""
|
||||
if self.is_bound:
|
||||
return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
|
||||
else:
|
||||
# Use the length of the initial data if it's there, 0 otherwise.
|
||||
initial_forms = len(self.initial) if self.initial else 0
|
||||
return initial_forms
|
||||
|
||||
@cached_property
|
||||
def forms(self):
|
||||
"""Instantiate forms at first property access."""
|
||||
# DoS protection is included in total_form_count()
|
||||
return [
|
||||
self._construct_form(i, **self.get_form_kwargs(i))
|
||||
for i in range(self.total_form_count())
|
||||
]
|
||||
|
||||
def get_form_kwargs(self, index):
|
||||
"""
|
||||
Return additional keyword arguments for each individual formset form.
|
||||
|
||||
index will be None if the form being constructed is a new empty
|
||||
form.
|
||||
"""
|
||||
return self.form_kwargs.copy()
|
||||
|
||||
def _construct_form(self, i, **kwargs):
|
||||
"""Instantiate and return the i-th form instance in a formset."""
|
||||
defaults = {
|
||||
"auto_id": self.auto_id,
|
||||
"prefix": self.add_prefix(i),
|
||||
"error_class": self.error_class,
|
||||
# Don't render the HTML 'required' attribute as it may cause
|
||||
# incorrect validation for extra, optional, and deleted
|
||||
# forms in the formset.
|
||||
"use_required_attribute": False,
|
||||
"renderer": self.renderer,
|
||||
}
|
||||
if self.is_bound:
|
||||
defaults["data"] = self.data
|
||||
defaults["files"] = self.files
|
||||
if self.initial and "initial" not in kwargs:
|
||||
try:
|
||||
defaults["initial"] = self.initial[i]
|
||||
except IndexError:
|
||||
pass
|
||||
# Allow extra forms to be empty, unless they're part of
|
||||
# the minimum forms.
|
||||
if i >= self.initial_form_count() and i >= self.min_num:
|
||||
defaults["empty_permitted"] = True
|
||||
defaults.update(kwargs)
|
||||
form = self.form(**defaults)
|
||||
self.add_fields(form, i)
|
||||
return form
|
||||
|
||||
@property
|
||||
def initial_forms(self):
|
||||
"""Return a list of all the initial forms in this formset."""
|
||||
return self.forms[: self.initial_form_count()]
|
||||
|
||||
@property
|
||||
def extra_forms(self):
|
||||
"""Return a list of all the extra forms in this formset."""
|
||||
return self.forms[self.initial_form_count() :]
|
||||
|
||||
@property
|
||||
def empty_form(self):
|
||||
form_kwargs = {
|
||||
**self.get_form_kwargs(None),
|
||||
"auto_id": self.auto_id,
|
||||
"prefix": self.add_prefix("__prefix__"),
|
||||
"empty_permitted": True,
|
||||
"use_required_attribute": False,
|
||||
"renderer": self.renderer,
|
||||
}
|
||||
form = self.form(**form_kwargs)
|
||||
self.add_fields(form, None)
|
||||
return form
|
||||
|
||||
@property
|
||||
def cleaned_data(self):
|
||||
"""
|
||||
Return a list of form.cleaned_data dicts for every form in self.forms.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
raise AttributeError(
|
||||
"'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__
|
||||
)
|
||||
return [form.cleaned_data for form in self.forms]
|
||||
|
||||
@property
|
||||
def deleted_forms(self):
|
||||
"""Return a list of forms that have been marked for deletion."""
|
||||
if not self.is_valid() or not self.can_delete:
|
||||
return []
|
||||
# construct _deleted_form_indexes which is just a list of form indexes
|
||||
# that have had their deletion widget set to True
|
||||
if not hasattr(self, "_deleted_form_indexes"):
|
||||
self._deleted_form_indexes = []
|
||||
for i, form in enumerate(self.forms):
|
||||
# if this is an extra form and hasn't changed, don't consider it
|
||||
if i >= self.initial_form_count() and not form.has_changed():
|
||||
continue
|
||||
if self._should_delete_form(form):
|
||||
self._deleted_form_indexes.append(i)
|
||||
return [self.forms[i] for i in self._deleted_form_indexes]
|
||||
|
||||
@property
|
||||
def ordered_forms(self):
|
||||
"""
|
||||
Return a list of form in the order specified by the incoming data.
|
||||
Raise an AttributeError if ordering is not allowed.
|
||||
"""
|
||||
if not self.is_valid() or not self.can_order:
|
||||
raise AttributeError(
|
||||
"'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__
|
||||
)
|
||||
# Construct _ordering, which is a list of (form_index, order_field_value)
|
||||
# tuples. After constructing this list, we'll sort it by order_field_value
|
||||
# so we have a way to get to the form indexes in the order specified
|
||||
# by the form data.
|
||||
if not hasattr(self, "_ordering"):
|
||||
self._ordering = []
|
||||
for i, form in enumerate(self.forms):
|
||||
# if this is an extra form and hasn't changed, don't consider it
|
||||
if i >= self.initial_form_count() and not form.has_changed():
|
||||
continue
|
||||
# don't add data marked for deletion to self.ordered_data
|
||||
if self.can_delete and self._should_delete_form(form):
|
||||
continue
|
||||
self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME]))
|
||||
# After we're done populating self._ordering, sort it.
|
||||
# A sort function to order things numerically ascending, but
|
||||
# None should be sorted below anything else. Allowing None as
|
||||
# a comparison value makes it so we can leave ordering fields
|
||||
# blank.
|
||||
|
||||
def compare_ordering_key(k):
|
||||
if k[1] is None:
|
||||
return (1, 0) # +infinity, larger than any number
|
||||
return (0, k[1])
|
||||
|
||||
self._ordering.sort(key=compare_ordering_key)
|
||||
# Return a list of form.cleaned_data dicts in the order specified by
|
||||
# the form data.
|
||||
return [self.forms[i[0]] for i in self._ordering]
|
||||
|
||||
@classmethod
|
||||
def get_default_prefix(cls):
|
||||
return "form"
|
||||
|
||||
@classmethod
|
||||
def get_deletion_widget(cls):
|
||||
return cls.deletion_widget
|
||||
|
||||
@classmethod
|
||||
def get_ordering_widget(cls):
|
||||
return cls.ordering_widget
|
||||
|
||||
def non_form_errors(self):
|
||||
"""
|
||||
Return an ErrorList of errors that aren't associated with a particular
|
||||
form -- i.e., from formset.clean(). Return an empty ErrorList if there
|
||||
are none.
|
||||
"""
|
||||
if self._non_form_errors is None:
|
||||
self.full_clean()
|
||||
return self._non_form_errors
|
||||
|
||||
@property
|
||||
def errors(self):
|
||||
"""Return a list of form.errors for every form in self.forms."""
|
||||
if self._errors is None:
|
||||
self.full_clean()
|
||||
return self._errors
|
||||
|
||||
def total_error_count(self):
|
||||
"""Return the number of errors across all forms in the formset."""
|
||||
return len(self.non_form_errors()) + sum(
|
||||
len(form_errors) for form_errors in self.errors
|
||||
)
|
||||
|
||||
def _should_delete_form(self, form):
|
||||
"""Return whether or not the form was marked for deletion."""
|
||||
return form.cleaned_data.get(DELETION_FIELD_NAME, False)
|
||||
|
||||
def is_valid(self):
|
||||
"""Return True if every form in self.forms is valid."""
|
||||
if not self.is_bound:
|
||||
return False
|
||||
# Accessing errors triggers a full clean the first time only.
|
||||
self.errors
|
||||
# List comprehension ensures is_valid() is called for all forms.
|
||||
# Forms due to be deleted shouldn't cause the formset to be invalid.
|
||||
forms_valid = all(
|
||||
[
|
||||
form.is_valid()
|
||||
for form in self.forms
|
||||
if not (self.can_delete and self._should_delete_form(form))
|
||||
]
|
||||
)
|
||||
return forms_valid and not self.non_form_errors()
|
||||
|
||||
def full_clean(self):
|
||||
"""
|
||||
Clean all of self.data and populate self._errors and
|
||||
self._non_form_errors.
|
||||
"""
|
||||
self._errors = []
|
||||
self._non_form_errors = self.error_class(
|
||||
error_class="nonform", renderer=self.renderer
|
||||
)
|
||||
empty_forms_count = 0
|
||||
|
||||
if not self.is_bound: # Stop further processing.
|
||||
return
|
||||
|
||||
if not self.management_form.is_valid():
|
||||
error = ValidationError(
|
||||
self.error_messages["missing_management_form"],
|
||||
params={
|
||||
"field_names": ", ".join(
|
||||
self.management_form.add_prefix(field_name)
|
||||
for field_name in self.management_form.errors
|
||||
),
|
||||
},
|
||||
code="missing_management_form",
|
||||
)
|
||||
self._non_form_errors.append(error)
|
||||
|
||||
for i, form in enumerate(self.forms):
|
||||
# Empty forms are unchanged forms beyond those with initial data.
|
||||
if not form.has_changed() and i >= self.initial_form_count():
|
||||
empty_forms_count += 1
|
||||
# Accessing errors calls full_clean() if necessary.
|
||||
# _should_delete_form() requires cleaned_data.
|
||||
form_errors = form.errors
|
||||
if self.can_delete and self._should_delete_form(form):
|
||||
continue
|
||||
self._errors.append(form_errors)
|
||||
try:
|
||||
if (
|
||||
self.validate_max
|
||||
and self.total_form_count() - len(self.deleted_forms) > self.max_num
|
||||
) or self.management_form.cleaned_data[
|
||||
TOTAL_FORM_COUNT
|
||||
] > self.absolute_max:
|
||||
raise ValidationError(
|
||||
self.error_messages["too_many_forms"] % {"num": self.max_num},
|
||||
code="too_many_forms",
|
||||
)
|
||||
if (
|
||||
self.validate_min
|
||||
and self.total_form_count()
|
||||
- len(self.deleted_forms)
|
||||
- empty_forms_count
|
||||
< self.min_num
|
||||
):
|
||||
raise ValidationError(
|
||||
self.error_messages["too_few_forms"] % {"num": self.min_num},
|
||||
code="too_few_forms",
|
||||
)
|
||||
# Give self.clean() a chance to do cross-form validation.
|
||||
self.clean()
|
||||
except ValidationError as e:
|
||||
self._non_form_errors = self.error_class(
|
||||
e.error_list,
|
||||
error_class="nonform",
|
||||
renderer=self.renderer,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Hook for doing any extra formset-wide cleaning after Form.clean() has
|
||||
been called on every form. Any ValidationError raised by this method
|
||||
will not be associated with a particular form; it will be accessible
|
||||
via formset.non_form_errors()
|
||||
"""
|
||||
pass
|
||||
|
||||
def has_changed(self):
|
||||
"""Return True if data in any form differs from initial."""
|
||||
return any(form.has_changed() for form in self)
|
||||
|
||||
def add_fields(self, form, index):
|
||||
"""A hook for adding extra fields on to each form instance."""
|
||||
initial_form_count = self.initial_form_count()
|
||||
if self.can_order:
|
||||
# Only pre-fill the ordering field for initial forms.
|
||||
if index is not None and index < initial_form_count:
|
||||
form.fields[ORDERING_FIELD_NAME] = IntegerField(
|
||||
label=_("Order"),
|
||||
initial=index + 1,
|
||||
required=False,
|
||||
widget=self.get_ordering_widget(),
|
||||
)
|
||||
else:
|
||||
form.fields[ORDERING_FIELD_NAME] = IntegerField(
|
||||
label=_("Order"),
|
||||
required=False,
|
||||
widget=self.get_ordering_widget(),
|
||||
)
|
||||
if self.can_delete and (
|
||||
self.can_delete_extra or (index is not None and index < initial_form_count)
|
||||
):
|
||||
form.fields[DELETION_FIELD_NAME] = BooleanField(
|
||||
label=_("Delete"),
|
||||
required=False,
|
||||
widget=self.get_deletion_widget(),
|
||||
)
|
||||
|
||||
def add_prefix(self, index):
|
||||
return "%s-%s" % (self.prefix, index)
|
||||
|
||||
def is_multipart(self):
|
||||
"""
|
||||
Return True if the formset needs to be multipart, i.e. it
|
||||
has FileInput, or False otherwise.
|
||||
"""
|
||||
if self.forms:
|
||||
return self.forms[0].is_multipart()
|
||||
else:
|
||||
return self.empty_form.is_multipart()
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
# All the forms on a FormSet are the same, so you only need to
|
||||
# interrogate the first form for media.
|
||||
if self.forms:
|
||||
return self.forms[0].media
|
||||
else:
|
||||
return self.empty_form.media
|
||||
|
||||
@property
|
||||
def template_name(self):
|
||||
return self.renderer.formset_template_name
|
||||
|
||||
def get_context(self):
|
||||
return {"formset": self}
|
||||
|
||||
|
||||
def formset_factory(
|
||||
form,
|
||||
formset=BaseFormSet,
|
||||
extra=1,
|
||||
can_order=False,
|
||||
can_delete=False,
|
||||
max_num=None,
|
||||
validate_max=False,
|
||||
min_num=None,
|
||||
validate_min=False,
|
||||
absolute_max=None,
|
||||
can_delete_extra=True,
|
||||
renderer=None,
|
||||
):
|
||||
"""Return a FormSet for the given form class."""
|
||||
if min_num is None:
|
||||
min_num = DEFAULT_MIN_NUM
|
||||
if max_num is None:
|
||||
max_num = DEFAULT_MAX_NUM
|
||||
# absolute_max is a hard limit on forms instantiated, to prevent
|
||||
# memory-exhaustion attacks. Default to max_num + DEFAULT_MAX_NUM
|
||||
# (which is 2 * DEFAULT_MAX_NUM if max_num is None in the first place).
|
||||
if absolute_max is None:
|
||||
absolute_max = max_num + DEFAULT_MAX_NUM
|
||||
if max_num > absolute_max:
|
||||
raise ValueError("'absolute_max' must be greater or equal to 'max_num'.")
|
||||
attrs = {
|
||||
"form": form,
|
||||
"extra": extra,
|
||||
"can_order": can_order,
|
||||
"can_delete": can_delete,
|
||||
"can_delete_extra": can_delete_extra,
|
||||
"min_num": min_num,
|
||||
"max_num": max_num,
|
||||
"absolute_max": absolute_max,
|
||||
"validate_min": validate_min,
|
||||
"validate_max": validate_max,
|
||||
"renderer": renderer or get_default_renderer(),
|
||||
}
|
||||
return type(form.__name__ + "FormSet", (formset,), attrs)
|
||||
|
||||
|
||||
def all_valid(formsets):
|
||||
"""Validate every formset and return True if all are valid."""
|
||||
# List comprehension ensures is_valid() is called for all formsets.
|
||||
return all([formset.is_valid() for formset in formsets])
|
@ -0,0 +1 @@
|
||||
{% for name, value in attrs.items() %}{% if value is not sameas False %} {{ name }}{% if value is not sameas True %}="{{ value }}"{% endif %}{% endif %}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/table.html" %}
|
@ -0,0 +1,24 @@
|
||||
{{ errors }}
|
||||
{% if errors and not fields %}
|
||||
<div>{% for field in hidden_fields %}{{ field }}{% endfor %}</div>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
<div{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}>
|
||||
{% if field.use_fieldset %}
|
||||
<fieldset>
|
||||
{% if field.label %}{{ field.legend_tag() }}{% endif %}
|
||||
{% else %}
|
||||
{% if field.label %}{{ field.label_tag() }}{% endif %}
|
||||
{% endif %}
|
||||
{% if field.help_text %}<div class="helptext">{{ field.help_text|safe }}</div>{% endif %}
|
||||
{{ errors }}
|
||||
{{ field }}
|
||||
{% if field.use_fieldset %}</fieldset>{% endif %}
|
||||
{% if loop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/errors/dict/ul.html" %}
|
@ -0,0 +1,3 @@
|
||||
{% for field, errors in errors %}* {{ field }}
|
||||
{% for error in errors %} * {{ error }}
|
||||
{% endfor %}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% if errors %}<ul class="{{ error_class }}">{% for field, error in errors %}<li>{{ field }}{{ error }}</li>{% endfor %}</ul>{% endif %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/errors/list/ul.html" %}
|
@ -0,0 +1,2 @@
|
||||
{% for error in errors %}* {{ error }}
|
||||
{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% if errors %}<ul class="{{ error_class }}">{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_div() }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_p() }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_table() }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_ul() }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% if use_tag %}<{{ tag }}{% if attrs %}{% include 'django/forms/attrs.html' %}{% endif %}>{{ label }}</{{ tag }}>{% else %}{{ label }}{% endif %}
|
@ -0,0 +1,20 @@
|
||||
{{ errors }}
|
||||
{% if errors and not fields %}
|
||||
<p>{% for field in hidden_fields %}{{ field }}{% endfor %}</p>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
{{ errors }}
|
||||
<p{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}>
|
||||
{% if field.label %}{{ field.label_tag() }}{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
{% if loop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1,29 @@
|
||||
{% if errors %}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
{{ errors }}
|
||||
{% if not fields %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
<tr{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}>
|
||||
<th>{% if field.label %}{{ field.label_tag() }}{% endif %}</th>
|
||||
<td>
|
||||
{{ errors }}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<br>
|
||||
<span class="helptext">{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
{% if loop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1,24 @@
|
||||
{% if errors %}
|
||||
<li>
|
||||
{{ errors }}
|
||||
{% if not fields %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
<li{% set classes = field.css_classes() %}{% if classes %} class="{{ classes }}"{% endif %}>
|
||||
{{ errors }}
|
||||
{% if field.label %}{{ field.label_tag() }}{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
{% if loop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1 @@
|
||||
{% for name, value in widget.attrs.items() %}{% if value is not sameas False %} {{ name }}{% if value is not sameas True %}="{{ value }}"{% endif %}{% endif %}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input_option.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/multiple_input.html" %}
|
@ -0,0 +1,5 @@
|
||||
{% if widget.is_initial %}{{ widget.initial_text }}: <a href="{{ widget.value.url }}">{{ widget.value }}</a>{% if not widget.required %}
|
||||
<input type="checkbox" name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}"{% if widget.attrs.disabled %} disabled{% endif %}>
|
||||
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}<br>
|
||||
{{ widget.input_text }}:{% endif %}
|
||||
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}>
|
@ -0,0 +1 @@
|
||||
{% if widget.wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} {{ widget.label }}</label>{% endif %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/multiwidget.html" %}
|
@ -0,0 +1,5 @@
|
||||
{% set id = widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
|
||||
<div><label>{{ group }}</label>{% endif %}{% for widget in options %}<div>
|
||||
{% include widget.template_name %}</div>{% endfor %}{% if group %}
|
||||
</div>{% endif %}{% endfor %}
|
||||
</div>
|
@ -0,0 +1 @@
|
||||
{% for widget in widget.subwidgets -%}{% include widget.template_name %}{%- endfor %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/multiple_input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input_option.html" %}
|
@ -0,0 +1,5 @@
|
||||
<select name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% for group_name, group_choices, group_index in widget.optgroups %}{% if group_name %}
|
||||
<optgroup label="{{ group_name }}">{% endif %}{% for widget in group_choices %}
|
||||
{% include widget.template_name %}{% endfor %}{% if group_name %}
|
||||
</optgroup>{% endif %}{% endfor %}
|
||||
</select>
|
@ -0,0 +1 @@
|
||||
{% include 'django/forms/widgets/multiwidget.html' %}
|
@ -0,0 +1 @@
|
||||
<option value="{{ widget.value }}"{% include "django/forms/widgets/attrs.html" %}>{{ widget.label }}</option>
|
@ -0,0 +1 @@
|
||||
{% include 'django/forms/widgets/multiwidget.html' %}
|
@ -0,0 +1 @@
|
||||
{% include 'django/forms/widgets/multiwidget.html' %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1,2 @@
|
||||
<textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
|
||||
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
1660
srcs/.venv/lib/python3.11/site-packages/django/forms/models.py
Normal file
1660
srcs/.venv/lib/python3.11/site-packages/django/forms/models.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,102 @@
|
||||
import functools
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.template.backends.django import DjangoTemplates
|
||||
from django.template.loader import get_template
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
def get_default_renderer():
|
||||
renderer_class = import_string(settings.FORM_RENDERER)
|
||||
return renderer_class()
|
||||
|
||||
|
||||
class BaseRenderer:
|
||||
# RemovedInDjango50Warning: When the deprecation ends, replace with
|
||||
# form_template_name = "django/forms/div.html"
|
||||
# formset_template_name = "django/forms/formsets/div.html"
|
||||
form_template_name = "django/forms/default.html"
|
||||
formset_template_name = "django/forms/formsets/default.html"
|
||||
|
||||
def get_template(self, template_name):
|
||||
raise NotImplementedError("subclasses must implement get_template()")
|
||||
|
||||
def render(self, template_name, context, request=None):
|
||||
template = self.get_template(template_name)
|
||||
return template.render(context, request=request).strip()
|
||||
|
||||
|
||||
class EngineMixin:
|
||||
def get_template(self, template_name):
|
||||
return self.engine.get_template(template_name)
|
||||
|
||||
@cached_property
|
||||
def engine(self):
|
||||
return self.backend(
|
||||
{
|
||||
"APP_DIRS": True,
|
||||
"DIRS": [Path(__file__).parent / self.backend.app_dirname],
|
||||
"NAME": "djangoforms",
|
||||
"OPTIONS": {},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class DjangoTemplates(EngineMixin, BaseRenderer):
|
||||
"""
|
||||
Load Django templates from the built-in widget templates in
|
||||
django/forms/templates and from apps' 'templates' directory.
|
||||
"""
|
||||
|
||||
backend = DjangoTemplates
|
||||
|
||||
|
||||
class Jinja2(EngineMixin, BaseRenderer):
|
||||
"""
|
||||
Load Jinja2 templates from the built-in widget templates in
|
||||
django/forms/jinja2 and from apps' 'jinja2' directory.
|
||||
"""
|
||||
|
||||
@cached_property
|
||||
def backend(self):
|
||||
from django.template.backends.jinja2 import Jinja2
|
||||
|
||||
return Jinja2
|
||||
|
||||
|
||||
class DjangoDivFormRenderer(DjangoTemplates):
|
||||
"""
|
||||
Load Django templates from django/forms/templates and from apps'
|
||||
'templates' directory and use the 'div.html' template to render forms and
|
||||
formsets.
|
||||
"""
|
||||
|
||||
# RemovedInDjango50Warning Deprecate this class in 5.0 and remove in 6.0.
|
||||
|
||||
form_template_name = "django/forms/div.html"
|
||||
formset_template_name = "django/forms/formsets/div.html"
|
||||
|
||||
|
||||
class Jinja2DivFormRenderer(Jinja2):
|
||||
"""
|
||||
Load Jinja2 templates from the built-in widget templates in
|
||||
django/forms/jinja2 and from apps' 'jinja2' directory.
|
||||
"""
|
||||
|
||||
# RemovedInDjango50Warning Deprecate this class in 5.0 and remove in 6.0.
|
||||
|
||||
form_template_name = "django/forms/div.html"
|
||||
formset_template_name = "django/forms/formsets/div.html"
|
||||
|
||||
|
||||
class TemplatesSetting(BaseRenderer):
|
||||
"""
|
||||
Load templates using template.loader.get_template() which is configured
|
||||
based on settings.TEMPLATES.
|
||||
"""
|
||||
|
||||
def get_template(self, template_name):
|
||||
return get_template(template_name)
|
@ -0,0 +1 @@
|
||||
{% for name, value in attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/table.html" %}
|
@ -0,0 +1,24 @@
|
||||
{{ errors }}
|
||||
{% if errors and not fields %}
|
||||
<div>{% for field in hidden_fields %}{{ field }}{% endfor %}</div>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
<div{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>
|
||||
{% if field.use_fieldset %}
|
||||
<fieldset>
|
||||
{% if field.label %}{{ field.legend_tag }}{% endif %}
|
||||
{% else %}
|
||||
{% if field.label %}{{ field.label_tag }}{% endif %}
|
||||
{% endif %}
|
||||
{% if field.help_text %}<div class="helptext">{{ field.help_text|safe }}</div>{% endif %}
|
||||
{{ errors }}
|
||||
{{ field }}
|
||||
{% if field.use_fieldset %}</fieldset>{% endif %}
|
||||
{% if forloop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/errors/dict/ul.html" %}
|
@ -0,0 +1,3 @@
|
||||
{% for field, errors in errors %}* {{ field }}
|
||||
{% for error in errors %} * {{ error }}
|
||||
{% endfor %}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% if errors %}<ul class="{{ error_class }}">{% for field, error in errors %}<li>{{ field }}{{ error }}</li>{% endfor %}</ul>{% endif %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/errors/list/ul.html" %}
|
@ -0,0 +1,2 @@
|
||||
{% for error in errors %}* {{ error }}
|
||||
{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% if errors %}<ul class="{{ error_class }}">{% for error in errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_div }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_p }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_table }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{{ formset.management_form }}{% for form in formset %}{{ form.as_ul }}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% if use_tag %}<{{ tag }}{% include 'django/forms/attrs.html' %}>{{ label }}</{{ tag }}>{% else %}{{ label }}{% endif %}
|
@ -0,0 +1,20 @@
|
||||
{{ errors }}
|
||||
{% if errors and not fields %}
|
||||
<p>{% for field in hidden_fields %}{{ field }}{% endfor %}</p>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
{{ errors }}
|
||||
<p{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>
|
||||
{% if field.label %}{{ field.label_tag }}{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
{% if forloop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1,29 @@
|
||||
{% if errors %}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
{{ errors }}
|
||||
{% if not fields %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
<tr{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>
|
||||
<th>{% if field.label %}{{ field.label_tag }}{% endif %}</th>
|
||||
<td>
|
||||
{{ errors }}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<br>
|
||||
<span class="helptext">{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
{% if forloop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1,24 @@
|
||||
{% if errors %}
|
||||
<li>
|
||||
{{ errors }}
|
||||
{% if not fields %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for field, errors in fields %}
|
||||
<li{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>
|
||||
{{ errors }}
|
||||
{% if field.label %}{{ field.label_tag }}{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext">{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
{% if forloop.last %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if not fields and not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
@ -0,0 +1 @@
|
||||
{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input_option.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/multiple_input.html" %}
|
@ -0,0 +1,5 @@
|
||||
{% if widget.is_initial %}{{ widget.initial_text }}: <a href="{{ widget.value.url }}">{{ widget.value }}</a>{% if not widget.required %}
|
||||
<input type="checkbox" name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}"{% if widget.attrs.disabled %} disabled{% endif %}>
|
||||
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}<br>
|
||||
{{ widget.input_text }}:{% endif %}
|
||||
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}>
|
@ -0,0 +1 @@
|
||||
{% if widget.wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} {{ widget.label }}</label>{% endif %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/multiwidget.html" %}
|
@ -0,0 +1,5 @@
|
||||
{% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
|
||||
<div><label>{{ group }}</label>{% endif %}{% for option in options %}<div>
|
||||
{% include option.template_name with widget=option %}</div>{% endfor %}{% if group %}
|
||||
</div>{% endif %}{% endfor %}
|
||||
</div>{% endwith %}
|
@ -0,0 +1 @@
|
||||
{% spaceless %}{% for widget in widget.subwidgets %}{% include widget.template_name %}{% endfor %}{% endspaceless %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/multiple_input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input_option.html" %}
|
@ -0,0 +1,5 @@
|
||||
<select name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% for group_name, group_choices, group_index in widget.optgroups %}{% if group_name %}
|
||||
<optgroup label="{{ group_name }}">{% endif %}{% for option in group_choices %}
|
||||
{% include option.template_name with widget=option %}{% endfor %}{% if group_name %}
|
||||
</optgroup>{% endif %}{% endfor %}
|
||||
</select>
|
@ -0,0 +1 @@
|
||||
{% include 'django/forms/widgets/multiwidget.html' %}
|
@ -0,0 +1 @@
|
||||
<option value="{{ widget.value|stringformat:'s' }}"{% include "django/forms/widgets/attrs.html" %}>{{ widget.label }}</option>
|
@ -0,0 +1 @@
|
||||
{% include 'django/forms/widgets/multiwidget.html' %}
|
@ -0,0 +1 @@
|
||||
{% include 'django/forms/widgets/multiwidget.html' %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1,2 @@
|
||||
<textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
|
||||
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
@ -0,0 +1 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
243
srcs/.venv/lib/python3.11/site-packages/django/forms/utils.py
Normal file
243
srcs/.venv/lib/python3.11/site-packages/django/forms/utils.py
Normal file
@ -0,0 +1,243 @@
|
||||
import json
|
||||
import warnings
|
||||
from collections import UserList
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.forms.renderers import get_default_renderer
|
||||
from django.utils import timezone
|
||||
from django.utils.deprecation import RemovedInDjango50Warning
|
||||
from django.utils.html import escape, format_html_join
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.version import get_docs_version
|
||||
|
||||
|
||||
def pretty_name(name):
|
||||
"""Convert 'first_name' to 'First name'."""
|
||||
if not name:
|
||||
return ""
|
||||
return name.replace("_", " ").capitalize()
|
||||
|
||||
|
||||
def flatatt(attrs):
|
||||
"""
|
||||
Convert a dictionary of attributes to a single string.
|
||||
The returned string will contain a leading space followed by key="value",
|
||||
XML-style pairs. In the case of a boolean value, the key will appear
|
||||
without a value. It is assumed that the keys do not need to be
|
||||
XML-escaped. If the passed dictionary is empty, then return an empty
|
||||
string.
|
||||
|
||||
The result is passed through 'mark_safe' (by way of 'format_html_join').
|
||||
"""
|
||||
key_value_attrs = []
|
||||
boolean_attrs = []
|
||||
for attr, value in attrs.items():
|
||||
if isinstance(value, bool):
|
||||
if value:
|
||||
boolean_attrs.append((attr,))
|
||||
elif value is not None:
|
||||
key_value_attrs.append((attr, value))
|
||||
|
||||
return format_html_join("", ' {}="{}"', sorted(key_value_attrs)) + format_html_join(
|
||||
"", " {}", sorted(boolean_attrs)
|
||||
)
|
||||
|
||||
|
||||
DEFAULT_TEMPLATE_DEPRECATION_MSG = (
|
||||
'The "default.html" templates for forms and formsets will be removed. These were '
|
||||
'proxies to the equivalent "table.html" templates, but the new "div.html" '
|
||||
"templates will be the default from Django 5.0. Transitional renderers are "
|
||||
"provided to allow you to opt-in to the new output style now. See "
|
||||
"https://docs.djangoproject.com/en/%s/releases/4.1/ for more details"
|
||||
% get_docs_version()
|
||||
)
|
||||
|
||||
|
||||
class RenderableMixin:
|
||||
def get_context(self):
|
||||
raise NotImplementedError(
|
||||
"Subclasses of RenderableMixin must provide a get_context() method."
|
||||
)
|
||||
|
||||
def render(self, template_name=None, context=None, renderer=None):
|
||||
renderer = renderer or self.renderer
|
||||
template = template_name or self.template_name
|
||||
context = context or self.get_context()
|
||||
if (
|
||||
template == "django/forms/default.html"
|
||||
or template == "django/forms/formsets/default.html"
|
||||
):
|
||||
warnings.warn(
|
||||
DEFAULT_TEMPLATE_DEPRECATION_MSG, RemovedInDjango50Warning, stacklevel=2
|
||||
)
|
||||
return mark_safe(renderer.render(template, context))
|
||||
|
||||
__str__ = render
|
||||
__html__ = render
|
||||
|
||||
|
||||
class RenderableFormMixin(RenderableMixin):
|
||||
def as_p(self):
|
||||
"""Render as <p> elements."""
|
||||
return self.render(self.template_name_p)
|
||||
|
||||
def as_table(self):
|
||||
"""Render as <tr> elements excluding the surrounding <table> tag."""
|
||||
return self.render(self.template_name_table)
|
||||
|
||||
def as_ul(self):
|
||||
"""Render as <li> elements excluding the surrounding <ul> tag."""
|
||||
return self.render(self.template_name_ul)
|
||||
|
||||
def as_div(self):
|
||||
"""Render as <div> elements."""
|
||||
return self.render(self.template_name_div)
|
||||
|
||||
|
||||
class RenderableErrorMixin(RenderableMixin):
|
||||
def as_json(self, escape_html=False):
|
||||
return json.dumps(self.get_json_data(escape_html))
|
||||
|
||||
def as_text(self):
|
||||
return self.render(self.template_name_text)
|
||||
|
||||
def as_ul(self):
|
||||
return self.render(self.template_name_ul)
|
||||
|
||||
|
||||
class ErrorDict(dict, RenderableErrorMixin):
|
||||
"""
|
||||
A collection of errors that knows how to display itself in various formats.
|
||||
|
||||
The dictionary keys are the field names, and the values are the errors.
|
||||
"""
|
||||
|
||||
template_name = "django/forms/errors/dict/default.html"
|
||||
template_name_text = "django/forms/errors/dict/text.txt"
|
||||
template_name_ul = "django/forms/errors/dict/ul.html"
|
||||
|
||||
def __init__(self, *args, renderer=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.renderer = renderer or get_default_renderer()
|
||||
|
||||
def as_data(self):
|
||||
return {f: e.as_data() for f, e in self.items()}
|
||||
|
||||
def get_json_data(self, escape_html=False):
|
||||
return {f: e.get_json_data(escape_html) for f, e in self.items()}
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
"errors": self.items(),
|
||||
"error_class": "errorlist",
|
||||
}
|
||||
|
||||
|
||||
class ErrorList(UserList, list, RenderableErrorMixin):
|
||||
"""
|
||||
A collection of errors that knows how to display itself in various formats.
|
||||
"""
|
||||
|
||||
template_name = "django/forms/errors/list/default.html"
|
||||
template_name_text = "django/forms/errors/list/text.txt"
|
||||
template_name_ul = "django/forms/errors/list/ul.html"
|
||||
|
||||
def __init__(self, initlist=None, error_class=None, renderer=None):
|
||||
super().__init__(initlist)
|
||||
|
||||
if error_class is None:
|
||||
self.error_class = "errorlist"
|
||||
else:
|
||||
self.error_class = "errorlist {}".format(error_class)
|
||||
self.renderer = renderer or get_default_renderer()
|
||||
|
||||
def as_data(self):
|
||||
return ValidationError(self.data).error_list
|
||||
|
||||
def copy(self):
|
||||
copy = super().copy()
|
||||
copy.error_class = self.error_class
|
||||
return copy
|
||||
|
||||
def get_json_data(self, escape_html=False):
|
||||
errors = []
|
||||
for error in self.as_data():
|
||||
message = next(iter(error))
|
||||
errors.append(
|
||||
{
|
||||
"message": escape(message) if escape_html else message,
|
||||
"code": error.code or "",
|
||||
}
|
||||
)
|
||||
return errors
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
"errors": self,
|
||||
"error_class": self.error_class,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return repr(list(self))
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in list(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return list(self) == other
|
||||
|
||||
def __getitem__(self, i):
|
||||
error = self.data[i]
|
||||
if isinstance(error, ValidationError):
|
||||
return next(iter(error))
|
||||
return error
|
||||
|
||||
def __reduce_ex__(self, *args, **kwargs):
|
||||
# The `list` reduce function returns an iterator as the fourth element
|
||||
# that is normally used for repopulating. Since we only inherit from
|
||||
# `list` for `isinstance` backward compatibility (Refs #17413) we
|
||||
# nullify this iterator as it would otherwise result in duplicate
|
||||
# entries. (Refs #23594)
|
||||
info = super(UserList, self).__reduce_ex__(*args, **kwargs)
|
||||
return info[:3] + (None, None)
|
||||
|
||||
|
||||
# Utilities for time zone support in DateTimeField et al.
|
||||
|
||||
|
||||
def from_current_timezone(value):
|
||||
"""
|
||||
When time zone support is enabled, convert naive datetimes
|
||||
entered in the current time zone to aware datetimes.
|
||||
"""
|
||||
if settings.USE_TZ and value is not None and timezone.is_naive(value):
|
||||
current_timezone = timezone.get_current_timezone()
|
||||
try:
|
||||
if not timezone._is_pytz_zone(
|
||||
current_timezone
|
||||
) and timezone._datetime_ambiguous_or_imaginary(value, current_timezone):
|
||||
raise ValueError("Ambiguous or non-existent time.")
|
||||
return timezone.make_aware(value, current_timezone)
|
||||
except Exception as exc:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"%(datetime)s couldn’t be interpreted "
|
||||
"in time zone %(current_timezone)s; it "
|
||||
"may be ambiguous or it may not exist."
|
||||
),
|
||||
code="ambiguous_timezone",
|
||||
params={"datetime": value, "current_timezone": current_timezone},
|
||||
) from exc
|
||||
return value
|
||||
|
||||
|
||||
def to_current_timezone(value):
|
||||
"""
|
||||
When time zone support is enabled, convert aware datetimes
|
||||
to naive datetimes in the current time zone for display.
|
||||
"""
|
||||
if settings.USE_TZ and value is not None and timezone.is_aware(value):
|
||||
return timezone.make_naive(value)
|
||||
return value
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user