Loading django/forms/forms.py +54 −5 Original line number Diff line number Diff line Loading @@ -138,6 +138,8 @@ class BaseForm(StrAndUnicode): "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." top_errors = self.non_field_errors() # Errors that should be displayed above all fields. output, hidden_fields = [], [] html_class_attr = '' for name, field in self.fields.items(): bf = BoundField(self, field, name) bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. Loading @@ -146,8 +148,15 @@ class BaseForm(StrAndUnicode): top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) hidden_fields.append(unicode(bf)) else: # Create a 'class="..."' atribute 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 % force_unicode(bf_errors)) if bf.label: label = conditional_escape(force_unicode(bf.label)) # Only add the suffix if the label does not end in Loading @@ -158,13 +167,23 @@ class BaseForm(StrAndUnicode): label = bf.label_tag(label) or '' else: label = '' if field.help_text: help_text = help_text_html % force_unicode(field.help_text) else: help_text = u'' output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text}) output.append(normal_row % { 'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text, 'html_class_attr': html_class_attr }) if top_errors: output.insert(0, error_row % force_unicode(top_errors)) if hidden_fields: # Insert any hidden fields in the last row. str_hidden = u''.join(hidden_fields) if output: Loading @@ -176,7 +195,9 @@ class BaseForm(StrAndUnicode): # 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': ''} last_row = (normal_row % {'errors': '', 'label': '', 'field': '', 'help_text':'', 'html_class_attr': html_class_attr}) output.append(last_row) output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender else: Loading @@ -187,15 +208,30 @@ class BaseForm(StrAndUnicode): def as_table(self): "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False) return self._html_output( normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', error_row = u'<tr><td colspan="2">%s</td></tr>', row_ender = u'</td></tr>', help_text_html = u'<br />%s', errors_on_separate_row = False) def as_ul(self): "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False) return self._html_output( normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', error_row = u'<li>%s</li>', row_ender = '</li>', help_text_html = u' %s', errors_on_separate_row = False) def as_p(self): "Returns this form rendered as HTML <p>s." return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True) return self._html_output( normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', error_row = u'%s', row_ender = '</p>', help_text_html = u' %s', errors_on_separate_row = True) def non_field_errors(self): """ Loading Loading @@ -433,6 +469,19 @@ class BoundField(StrAndUnicode): contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents)) return mark_safe(contents) def css_classes(self, extra_classes=None): """ Returns 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) def _is_hidden(self): "Returns True if this BoundField's widget is hidden." return self.field.widget.is_hidden Loading docs/ref/forms/api.txt +30 −0 Original line number Diff line number Diff line Loading @@ -366,6 +366,36 @@ calls its ``as_table()`` method behind the scenes:: <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr> <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> Styling required or erroneous form rows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 1.2 It's pretty common to style form rows and fields that are required or have errors. For example, you might want to present required form rows in bold and highlight errors in red. The :class:`Form` class has a couple of hooks you can use to add ``class`` attributes to required rows or to rows with errors: simple set the :attr:`Form.error_css_class` and/or :attr:`Form.required_css_class` attributes:: class ContactForm(Form): error_css_class = 'error' required_css_class = 'required' # ... and the rest of your fields here Once you've done that, rows will be given ``"error"`` and/or ``"required"`` classes, as needed. The HTML will look something like:: >>> f = ContactForm(data) >>> print f.as_table() <tr class="required"><th><label for="id_subject">Subject:</label> ... <tr class="required"><th><label for="id_message">Message:</label> ... <tr class="required error"><th><label for="id_sender">Sender:</label> ... <tr><th><label for="id_cc_myself">Cc myself:<label> ... .. _ref-forms-api-configuring-label: Configuring HTML ``<label>`` tags Loading tests/regressiontests/forms/forms.py +32 −0 Original line number Diff line number Diff line Loading @@ -1814,4 +1814,36 @@ True >>> print MyForm() <tr><th><label for="id_field1">Field1:</label></th><td><input id="id_field1" type="text" name="field1" maxlength="50" /><input type="hidden" name="initial-field1" id="initial-id_field1" /></td></tr> # The error_html_class and required_html_class attributes #################### >>> p = Person({}) >>> p.error_css_class = 'error' >>> p.required_css_class = 'required' >>> print p.as_ul() <li class="required error"><ul class="errorlist"><li>This field is required.</li></ul><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li> <li class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool"> <option value="1" selected="selected">Unknown</option> <option value="2">Yes</option> <option value="3">No</option> </select></li> >>> print p.as_p() <ul class="errorlist"><li>This field is required.</li></ul> <p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p> <p class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool"> <option value="1" selected="selected">Unknown</option> <option value="2">Yes</option> <option value="3">No</option> </select></p> >>> print p.as_table() <tr class="required error"><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" id="id_name" /></td></tr> <tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool"> <option value="1" selected="selected">Unknown</option> <option value="2">Yes</option> <option value="3">No</option> </select></td></tr> """ Loading
django/forms/forms.py +54 −5 Original line number Diff line number Diff line Loading @@ -138,6 +138,8 @@ class BaseForm(StrAndUnicode): "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." top_errors = self.non_field_errors() # Errors that should be displayed above all fields. output, hidden_fields = [], [] html_class_attr = '' for name, field in self.fields.items(): bf = BoundField(self, field, name) bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable. Loading @@ -146,8 +148,15 @@ class BaseForm(StrAndUnicode): top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) hidden_fields.append(unicode(bf)) else: # Create a 'class="..."' atribute 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 % force_unicode(bf_errors)) if bf.label: label = conditional_escape(force_unicode(bf.label)) # Only add the suffix if the label does not end in Loading @@ -158,13 +167,23 @@ class BaseForm(StrAndUnicode): label = bf.label_tag(label) or '' else: label = '' if field.help_text: help_text = help_text_html % force_unicode(field.help_text) else: help_text = u'' output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text}) output.append(normal_row % { 'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text, 'html_class_attr': html_class_attr }) if top_errors: output.insert(0, error_row % force_unicode(top_errors)) if hidden_fields: # Insert any hidden fields in the last row. str_hidden = u''.join(hidden_fields) if output: Loading @@ -176,7 +195,9 @@ class BaseForm(StrAndUnicode): # 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': ''} last_row = (normal_row % {'errors': '', 'label': '', 'field': '', 'help_text':'', 'html_class_attr': html_class_attr}) output.append(last_row) output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender else: Loading @@ -187,15 +208,30 @@ class BaseForm(StrAndUnicode): def as_table(self): "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False) return self._html_output( normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', error_row = u'<tr><td colspan="2">%s</td></tr>', row_ender = u'</td></tr>', help_text_html = u'<br />%s', errors_on_separate_row = False) def as_ul(self): "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False) return self._html_output( normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', error_row = u'<li>%s</li>', row_ender = '</li>', help_text_html = u' %s', errors_on_separate_row = False) def as_p(self): "Returns this form rendered as HTML <p>s." return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True) return self._html_output( normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', error_row = u'%s', row_ender = '</p>', help_text_html = u' %s', errors_on_separate_row = True) def non_field_errors(self): """ Loading Loading @@ -433,6 +469,19 @@ class BoundField(StrAndUnicode): contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents)) return mark_safe(contents) def css_classes(self, extra_classes=None): """ Returns 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) def _is_hidden(self): "Returns True if this BoundField's widget is hidden." return self.field.widget.is_hidden Loading
docs/ref/forms/api.txt +30 −0 Original line number Diff line number Diff line Loading @@ -366,6 +366,36 @@ calls its ``as_table()`` method behind the scenes:: <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr> <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> Styling required or erroneous form rows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 1.2 It's pretty common to style form rows and fields that are required or have errors. For example, you might want to present required form rows in bold and highlight errors in red. The :class:`Form` class has a couple of hooks you can use to add ``class`` attributes to required rows or to rows with errors: simple set the :attr:`Form.error_css_class` and/or :attr:`Form.required_css_class` attributes:: class ContactForm(Form): error_css_class = 'error' required_css_class = 'required' # ... and the rest of your fields here Once you've done that, rows will be given ``"error"`` and/or ``"required"`` classes, as needed. The HTML will look something like:: >>> f = ContactForm(data) >>> print f.as_table() <tr class="required"><th><label for="id_subject">Subject:</label> ... <tr class="required"><th><label for="id_message">Message:</label> ... <tr class="required error"><th><label for="id_sender">Sender:</label> ... <tr><th><label for="id_cc_myself">Cc myself:<label> ... .. _ref-forms-api-configuring-label: Configuring HTML ``<label>`` tags Loading
tests/regressiontests/forms/forms.py +32 −0 Original line number Diff line number Diff line Loading @@ -1814,4 +1814,36 @@ True >>> print MyForm() <tr><th><label for="id_field1">Field1:</label></th><td><input id="id_field1" type="text" name="field1" maxlength="50" /><input type="hidden" name="initial-field1" id="initial-id_field1" /></td></tr> # The error_html_class and required_html_class attributes #################### >>> p = Person({}) >>> p.error_css_class = 'error' >>> p.required_css_class = 'required' >>> print p.as_ul() <li class="required error"><ul class="errorlist"><li>This field is required.</li></ul><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li> <li class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool"> <option value="1" selected="selected">Unknown</option> <option value="2">Yes</option> <option value="3">No</option> </select></li> >>> print p.as_p() <ul class="errorlist"><li>This field is required.</li></ul> <p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p> <p class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool"> <option value="1" selected="selected">Unknown</option> <option value="2">Yes</option> <option value="3">No</option> </select></p> >>> print p.as_table() <tr class="required error"><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" id="id_name" /></td></tr> <tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool"> <option value="1" selected="selected">Unknown</option> <option value="2">Yes</option> <option value="3">No</option> </select></td></tr> """