summaryrefslogtreecommitdiff
path: root/django/forms/utils.py
diff options
context:
space:
mode:
authorDavid Smith <smithdc@gmail.com>2021-09-10 08:06:01 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-09-20 15:50:18 +0200
commit456466d932830b096d39806e291fe23ec5ed38d5 (patch)
tree9320cc645ef43eb920630cff02c1387b34f21906 /django/forms/utils.py
parent5353e7c2505c0d0ab8232ad9c131b3c99c833988 (diff)
Fixed #31026 -- Switched form rendering to template engine.
Thanks Carlton Gibson, Keryn Knight, Mariusz Felisiak, and Nick Pope for reviews. Co-authored-by: Johannes Hoppe <info@johanneshoppe.com>
Diffstat (limited to 'django/forms/utils.py')
-rw-r--r--django/forms/utils.py116
1 files changed, 71 insertions, 45 deletions
diff --git a/django/forms/utils.py b/django/forms/utils.py
index 50412f414b..44447b5cf5 100644
--- a/django/forms/utils.py
+++ b/django/forms/utils.py
@@ -1,10 +1,12 @@
import json
-from collections import UserList
+from collections import UserDict, 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, format_html_join, html_safe
+from django.utils.html import escape, format_html_join
+from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
@@ -41,53 +43,90 @@ def flatatt(attrs):
)
-@html_safe
-class ErrorDict(dict):
+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):
+ return mark_safe((renderer or self.renderer).render(
+ template_name or self.template_name,
+ context or self.get_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)
+
+
+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(UserDict, 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, data=None, renderer=None):
+ super().__init__(data)
+ 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 as_json(self, escape_html=False):
- return json.dumps(self.get_json_data(escape_html))
-
- def as_ul(self):
- if not self:
- return ''
- return format_html(
- '<ul class="errorlist">{}</ul>',
- format_html_join('', '<li>{}{}</li>', self.items())
- )
-
- def as_text(self):
- output = []
- for field, errors in self.items():
- output.append('* %s' % field)
- output.append('\n'.join(' * %s' % e for e in errors))
- return '\n'.join(output)
+ def get_context(self):
+ return {
+ 'errors': self.items(),
+ 'error_class': 'errorlist',
+ }
- def __str__(self):
- return self.as_ul()
-
-@html_safe
-class ErrorList(UserList, list):
+class ErrorList(UserList, list, RenderableErrorMixin):
"""
A collection of errors that knows how to display itself in various formats.
"""
- def __init__(self, initlist=None, error_class=None):
+ 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
@@ -107,24 +146,11 @@ class ErrorList(UserList, list):
})
return errors
- def as_json(self, escape_html=False):
- return json.dumps(self.get_json_data(escape_html))
-
- def as_ul(self):
- if not self.data:
- return ''
-
- return format_html(
- '<ul class="{}">{}</ul>',
- self.error_class,
- format_html_join('', '<li>{}</li>', ((e,) for e in self))
- )
-
- def as_text(self):
- return '\n'.join('* %s' % e for e in self)
-
- def __str__(self):
- return self.as_ul()
+ def get_context(self):
+ return {
+ 'errors': self,
+ 'error_class': self.error_class,
+ }
def __repr__(self):
return repr(list(self))