1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
import json
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.html import escape, format_html_join
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
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)
)
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()
return mark_safe(renderer.render(template, context))
__str__ = render
__html__ = render
class RenderableFieldMixin(RenderableMixin):
def as_field_group(self):
return self.render()
def as_hidden(self):
raise NotImplementedError(
"Subclasses of RenderableFieldMixin must provide an as_hidden() method."
)
def as_widget(self):
raise NotImplementedError(
"Subclasses of RenderableFieldMixin must provide an as_widget() method."
)
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()
__html__ = __str__
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, field_id=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()
self.field_id = field_id
def as_data(self):
return ValidationError(self.data).error_list
def copy(self):
copy = super().copy()
copy.error_class = self.error_class
copy.renderer = self.renderer
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 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
|