diff options
| author | David Smith <smithdc@gmail.com> | 2021-09-10 08:06:01 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-09-20 15:50:18 +0200 |
| commit | 456466d932830b096d39806e291fe23ec5ed38d5 (patch) | |
| tree | 9320cc645ef43eb920630cff02c1387b34f21906 /django/forms/forms.py | |
| parent | 5353e7c2505c0d0ab8232ad9c131b3c99c833988 (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/forms.py')
| -rw-r--r-- | django/forms/forms.py | 95 |
1 files changed, 55 insertions, 40 deletions
diff --git a/django/forms/forms.py b/django/forms/forms.py index 2bf268ae76..589b4693fd 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -4,15 +4,17 @@ 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 +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, html_safe -from django.utils.safestring import mark_safe +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 @@ -49,8 +51,7 @@ class DeclarativeFieldsMetaclass(MediaDefiningClass): return new_class -@html_safe -class BaseForm: +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 @@ -62,6 +63,12 @@ class BaseForm: prefix = None use_required_attribute = True + template_name = 'django/forms/default.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): @@ -129,9 +136,6 @@ class BaseForm: fields.update(self.fields) # add remaining fields in original order self.fields = fields - def __str__(self): - return self.as_table() - def __repr__(self): if self._errors is None: is_valid = "Unknown" @@ -206,6 +210,12 @@ class BaseForm: 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 = [], [] @@ -282,35 +292,37 @@ class BaseForm: output.append(str_hidden) return mark_safe('\n'.join(output)) - def as_table(self): - "Return this form rendered as HTML <tr>s -- excluding the <table></table>." - return self._html_output( - normal_row='<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', - error_row='<tr><td colspan="2">%s</td></tr>', - row_ender='</td></tr>', - help_text_html='<br><span class="helptext">%s</span>', - errors_on_separate_row=False, - ) - - def as_ul(self): - "Return this form rendered as HTML <li>s -- excluding the <ul></ul>." - return self._html_output( - normal_row='<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', - error_row='<li>%s</li>', - row_ender='</li>', - help_text_html=' <span class="helptext">%s</span>', - errors_on_separate_row=False, - ) - - def as_p(self): - "Return this form rendered as HTML <p>s." - return self._html_output( - normal_row='<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', - error_row='%s', - row_ender='</p>', - help_text_html=' <span class="helptext">%s</span>', - errors_on_separate_row=True, - ) + 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): """ @@ -318,7 +330,10 @@ class BaseForm: 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')) + return self.errors.get( + NON_FIELD_ERRORS, + self.error_class(error_class='nonfield', renderer=self.renderer), + ) def add_error(self, field, error): """ @@ -360,9 +375,9 @@ class BaseForm: 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') + self._errors[field] = self.error_class(error_class='nonfield', renderer=self.renderer) else: - self._errors[field] = self.error_class() + 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] |
