From 92803205cbcaaee16ac0eb724c45019a9d896aac Mon Sep 17 00:00:00 2001
From: Jacob Kaplan-Moss
Date: Sat, 12 Dec 2009 18:52:12 +0000
Subject: [PATCH] Fixed #3512: it's now possible to add CSS hooks to
required/erroneous form rows. Thanks, SmileyChris.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@11830 bcc190cf-cafb-0310-a4f2-bffc1f526a37
---
django/forms/forms.py | 59 +++++++++++++++++++++++++---
docs/ref/forms/api.txt | 30 ++++++++++++++
tests/regressiontests/forms/forms.py | 32 +++++++++++++++
3 files changed, 116 insertions(+), 5 deletions(-)
diff --git a/django/forms/forms.py b/django/forms/forms.py
index 705058a6e6..e854de8a7a 100644
--- a/django/forms/forms.py
+++ b/django/forms/forms.py
@@ -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.
@@ -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
@@ -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:
@@ -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:
@@ -187,15 +208,30 @@ class BaseForm(StrAndUnicode):
def as_table(self):
"Returns this form rendered as HTML
',
+ error_row = u'%s',
+ row_ender = '',
+ help_text_html = u' %s',
+ errors_on_separate_row = True)
def non_field_errors(self):
"""
@@ -433,6 +469,19 @@ class BoundField(StrAndUnicode):
contents = u'' % (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
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
index 7a2341f69b..26934f07a3 100644
--- a/docs/ref/forms/api.txt
+++ b/docs/ref/forms/api.txt
@@ -366,6 +366,36 @@ calls its ``as_table()`` method behind the scenes::
+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()
+