Move "(required)" text to third column instead of second column in templatetags, it's much more user friendly that way. Add new version of as_table that support our current error messages format, information about required fields and tooltips.
Patch by: Pawel Solyga
--- a/app/soc/content/css/soc-081108.css Mon Nov 10 23:18:06 2008 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,457 +0,0 @@
-/*
-Copyright 2008 the Melange authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- */
-
-/*
- * STANDARD HTML TAGS
- */
-
-body {
- background-color: white;
- color: black;
-
- font-family: Arial, sans-serif;
- font-size: medium;
-
- margin: 8px;
- margin-top: 3px;
-}
-
-img {
- border: 0;
-}
-
-form {
- margin: 0;
- padding: 0;
-}
-
-li {
- margin-bottom: 0.25em;
-}
-
-/* HEADERS */
-h1 {
- font-size: x-large;
- margin-top: 0px;
-}
-
-h2 {
- font-size: large;
-}
-
-h3 {
- font-size: medium;
-}
-
-h4 {
- font-size: small;
-}
-
-/* PRE-FORMATTED TEXT */
-pre, code {
- color: #007000;
- font-family: "bogus font here", monospace;
- font-size: 100%;
-}
-
-pre {
- border: 1px solid silver;
- background-color: #f5f5f5;
- padding: 0.5em;
- overflow: auto;
- margin: 2em;
-}
-
-pre ins {
- color: #cc0000;
- font-weight: bold;
- text-decoration: none;
-}
-
-/* LINKS AND ANCHORS */
-a:link {
- color: #0000cc;
-}
-
-a:active {
- color: #cc0000;
-}
-
-a:visited {
- color: #551a8b;
-}
-
-a.selected, .selected a, .selected {
- color: black;
- font-weight: bold;
- text-decoration: none;
-}
-
-a.novisit {
- color: #2a55a3;
-}
-
-a.noul, a.noulv {
- color: #4182fa; /* #93b7fa; */
- text-decoration: none;
-}
-
-a:hover.noul {
- text-decoration: underline;
-}
-
-a:visited.noul {
- color: #a32a91; /* #2a55a3; */
-}
-
-
-/* Styles used by Django Forms */
-ul.errorlist {
- color: #FF0000;
- font-size: small;
-}
-
-
-/* TABLES */
-table {
- border-collapse: collapse;
-}
-
-th, td {
- /*padding: 0;*/
- padding:2px 5px;
- vertical-align: top;
- text-align: left;
-}
-
-/* FORM TABLE FIELDS */
-td.formfieldrequired {
- font-style: italic;
- font-size: small;
-}
-
-td.formfieldhelptext {
- font-style: italic;
- font-size: small;
-}
-
-td.formfielderror {
- color: #FF0000;
- font-size: small;
-}
-
-td.formfieldheading {
- font-weight: bold;
- font-size: small;
-}
-
-td.formfieldlabel {
- font-weight: bold;
- font-size: small;
-}
-
-td.formfielderrorlabel {
- font-weight: bold;
- color: #FF0000;
- font-size: small;
-}
-
-/* TABLE QUEUES (used with .list) */
-table#queues {
- border-collapse: collapse;
- width: 100%;
-}
-
-table#queues tr {
- border-bottom: thin solid lightgray;
-}
-
-table#queues td {
- padding: 2px;
-}
-
-/*
- * PAGE ELEMENTS
- */
-
-#title {
- border-top: 1px solid #3366cc;
- background-color: #e5ecf9;
- font-size: large;
- font-weight: bold;
- margin: 0;
- padding: 0;
- padding-top: 1px;
- padding-bottom: 1px;
- margin-top: 5px;
- margin-left: 200px;
- padding-left: 3px;
-}
-
-#notice {
- margin-left: 200px;
- padding: 3px;
-}
-
-#logo {
- padding-right: 18px;
- position: absolute;
- left: 0;
- top: -5px;
-}
-
-#login {
- text-align: right;
-}
-
-#badge {
- clear: both;
- margin-top: 3.5em;
- margin-bottom: 1em;
- height: 53px;
- font-style: italic;
-}
-
-#body {
- border-left: 1px dotted silver;
- margin-left: 200px;
- margin-right: 25px;
- padding-left: 18px;
- padding-bottom: 25px;
-}
-
-#body .buttons {
- margin-right: 4px;
- margin-top: 20px;
-}
-
-#body a.button, input[type^="submit"], input[type^="button"] {
- margin: 0;
- padding: 2px 5px 2px 5px;
- font-family: Arial, Sans-serif;
- font-size: 12px;
- text-decoration: none;
- color: #222;
- cursor: default;
- background: #ddd url("/soc/content/images/button-background.gif") repeat-x 0 0;
- border: 1px solid #aaa;
-}
-
-#body a.button:hover, input[type^="submit"]:hover, input[type^="button"]:hover {
- border-color: #9cf #69e #69e #7af;
-}
-
-#header {
- height: 50px;
- margin-bottom: 11px;
- position: relative;
-}
-
-#footer {
- clear: both;
- text-align: center;
- margin-top: 3.5em;
- margin-bottom: 1em;
- background-image: url("http://www.google.com/images/art.gif");
- height: 53px;
- background-repeat: no-repeat;
- background-position: left center;
-}
-
-#footer .text {
- padding-top: 20px;
-}
-
-#created {
- font-size: small;
-}
-
-/* SIDEBAR MENU */
-#side {
- width: 200px;
- margin-bottom: 3em;
- float: left;
- font-size: small;
-}
-
-#menu ul {
- margin: 0;
- padding: 0;
- list-style-type: none;
- margin-bottom: 1em;
- font-size: 95%;
-}
-
-#menu ul ul {
- margin-left: 10px;
- margin-bottom: 0;
-}
-
-#menu li {
- margin-top: 4px;
- list-style-type: none;
- list-style-image: none;
-}
-
-#menu img {
- margin-right: 4px;
-}
-
-#menu li.leaf {
- padding-left: 14px;
-}
-
-#menu h4 {
- margin: 0;
- padding: 0;
- margin-bottom: 1em;
-}
-
-
-/* SEARCH FIELD */
-#search {
- margin-top: 2em;
-}
-
-#search .header {
- font-weight: bold;
- font-size: 90%;
- margin-bottom: 1px;
-}
-
-#search .button {
- margin-top: 1px;
-}
-
-#search .input input {
- width: 125px;
-}
-
-/* BLOG FEEDS */
-.blog {
- border: 10px solid #e5ecf9;
- border-top: 1px solid #3366cc;
-}
-
-.blog h2 {
- margin-top: 0.1em;
- background-color: #e5ecf9;
-}
-
-.blog h2 a {
- text-decoration: none;
- color: black;
-}
-
-.blog h2 a:visited {
- text-decoration: none;
- color: black;
-}
-
-
-.blog .entry {
- margin-bottom: 1em;
-}
-
-.blog .title {
- font-size: medium;
-}
-
-.blog .author {
- color: gray;
- margin-bottom: 0.5em;
-}
-
-.blog .snippet {
- background-color: white;
-}
-
-/* LIST */
-.list {
- background-color: #E5ECF9;
- border: 1px solid #93b7fa;
- border-bottom: 2px solid #93b7fa;
- padding: 3px;
- -moz-border-radius: 5px 5px 0px 0px;
-}
-
-.list .pagination {
- text-align: right;
- padding: 3px;
-}
-
-.list table{
- background-color: white;
-}
-
-.list table th {
- background-color: #eeeeec;
- border-right: 1px solid lightgray;
- border-top: 1px solid lightgray;
-}
-
-.list table tr.on {
- background-color: #ff9;
-}
-
-.list table tr.off {
- background-color: #fff;
-}
-
-.list table td.last {
- border-right: 1px solid lightgray;
-}
-
-.list table .first {
- border-left: 1px solid lightgray;
-}
-
-/*
- * CUSTOM CLASSES
- */
-
-.todo {
- color: #cc0000;
- font-size: 80%;
-}
-
-.newmark {
- color: red;
- font-size: 80%;
- vertical-align: top;
-}
-
-.error {
- color: red;
-}
-
-.notice {
- background:#fad163;
- font-size: small;
- font-weight: bold;
-}
-
-.rounded_ul { background: url(/soc/content/images/ul.gif) no-repeat top left; }
-.rounded_ur { background: url(/soc/content/images/ur.gif) no-repeat top right; }
-.rounded_ll { background: url(/soc/content/images/ll.gif) no-repeat bottom left; }
-.rounded_lr { background: url(/soc/content/images/lr.gif) no-repeat bottom right; }
-
-/* Disabled text. */
-.disabled {
- color: gray;
-}
-
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/content/css/soc-081111.css Tue Nov 11 01:02:56 2008 +0000
@@ -0,0 +1,457 @@
+/*
+Copyright 2008 the Melange authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ */
+
+/*
+ * STANDARD HTML TAGS
+ */
+
+body {
+ background-color: white;
+ color: black;
+
+ font-family: Arial, sans-serif;
+ font-size: medium;
+
+ margin: 8px;
+ margin-top: 3px;
+}
+
+img {
+ border: 0;
+}
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+li {
+ margin-bottom: 0.25em;
+}
+
+/* HEADERS */
+h1 {
+ font-size: x-large;
+ margin-top: 0px;
+}
+
+h2 {
+ font-size: large;
+}
+
+h3 {
+ font-size: medium;
+}
+
+h4 {
+ font-size: small;
+}
+
+/* PRE-FORMATTED TEXT */
+pre, code {
+ color: #007000;
+ font-family: "bogus font here", monospace;
+ font-size: 100%;
+}
+
+pre {
+ border: 1px solid silver;
+ background-color: #f5f5f5;
+ padding: 0.5em;
+ overflow: auto;
+ margin: 2em;
+}
+
+pre ins {
+ color: #cc0000;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+/* LINKS AND ANCHORS */
+a:link {
+ color: #0000cc;
+}
+
+a:active {
+ color: #cc0000;
+}
+
+a:visited {
+ color: #551a8b;
+}
+
+a.selected, .selected a, .selected {
+ color: black;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+a.novisit {
+ color: #2a55a3;
+}
+
+a.noul, a.noulv {
+ color: #4182fa; /* #93b7fa; */
+ text-decoration: none;
+}
+
+a:hover.noul {
+ text-decoration: underline;
+}
+
+a:visited.noul {
+ color: #a32a91; /* #2a55a3; */
+}
+
+
+/* Styles used by Django Forms */
+ul.errorlist {
+ color: #FF0000;
+ font-size: small;
+}
+
+
+/* TABLES */
+table {
+ border-collapse: collapse;
+}
+
+th, td {
+ /*padding: 0;*/
+ padding:2px 5px;
+ vertical-align: top;
+ text-align: left;
+}
+
+/* FORM FIELDS */
+td.formfieldrequired {
+ font-style: italic;
+ font-size: small;
+}
+
+td.formfieldhelptext {
+ font-style: italic;
+ font-size: small;
+}
+
+td.formfielderror {
+ color: #FF0000;
+ font-size: small;
+}
+
+td.formfieldheading {
+ font-weight: bold;
+ font-size: small;
+}
+
+td.formfieldlabel {
+ font-weight: bold;
+ font-size: small;
+}
+
+td.formfielderrorlabel, span.formfielderrorlabel {
+ font-weight: bold;
+ color: #FF0000;
+ font-size: small;
+}
+
+/* TABLE QUEUES (used with .list) */
+table#queues {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+table#queues tr {
+ border-bottom: thin solid lightgray;
+}
+
+table#queues td {
+ padding: 2px;
+}
+
+/*
+ * PAGE ELEMENTS
+ */
+
+#title {
+ border-top: 1px solid #3366cc;
+ background-color: #e5ecf9;
+ font-size: large;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ margin-top: 5px;
+ margin-left: 200px;
+ padding-left: 3px;
+}
+
+#notice {
+ margin-left: 200px;
+ padding: 3px;
+}
+
+#logo {
+ padding-right: 18px;
+ position: absolute;
+ left: 0;
+ top: -5px;
+}
+
+#login {
+ text-align: right;
+}
+
+#badge {
+ clear: both;
+ margin-top: 3.5em;
+ margin-bottom: 1em;
+ height: 53px;
+ font-style: italic;
+}
+
+#body {
+ border-left: 1px dotted silver;
+ margin-left: 200px;
+ margin-right: 25px;
+ padding-left: 18px;
+ padding-bottom: 25px;
+}
+
+#body .buttons {
+ margin-right: 4px;
+ margin-top: 20px;
+}
+
+#body a.button, input[type^="submit"], input[type^="button"] {
+ margin: 0;
+ padding: 2px 5px 2px 5px;
+ font-family: Arial, Sans-serif;
+ font-size: 12px;
+ text-decoration: none;
+ color: #222;
+ cursor: default;
+ background: #ddd url("/soc/content/images/button-background.gif") repeat-x 0 0;
+ border: 1px solid #aaa;
+}
+
+#body a.button:hover, input[type^="submit"]:hover, input[type^="button"]:hover {
+ border-color: #9cf #69e #69e #7af;
+}
+
+#header {
+ height: 50px;
+ margin-bottom: 11px;
+ position: relative;
+}
+
+#footer {
+ clear: both;
+ text-align: center;
+ margin-top: 3.5em;
+ margin-bottom: 1em;
+ background-image: url("http://www.google.com/images/art.gif");
+ height: 53px;
+ background-repeat: no-repeat;
+ background-position: left center;
+}
+
+#footer .text {
+ padding-top: 20px;
+}
+
+#created {
+ font-size: small;
+}
+
+/* SIDEBAR MENU */
+#side {
+ width: 200px;
+ margin-bottom: 3em;
+ float: left;
+ font-size: small;
+}
+
+#menu ul {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+ margin-bottom: 1em;
+ font-size: 95%;
+}
+
+#menu ul ul {
+ margin-left: 10px;
+ margin-bottom: 0;
+}
+
+#menu li {
+ margin-top: 4px;
+ list-style-type: none;
+ list-style-image: none;
+}
+
+#menu img {
+ margin-right: 4px;
+}
+
+#menu li.leaf {
+ padding-left: 14px;
+}
+
+#menu h4 {
+ margin: 0;
+ padding: 0;
+ margin-bottom: 1em;
+}
+
+
+/* SEARCH FIELD */
+#search {
+ margin-top: 2em;
+}
+
+#search .header {
+ font-weight: bold;
+ font-size: 90%;
+ margin-bottom: 1px;
+}
+
+#search .button {
+ margin-top: 1px;
+}
+
+#search .input input {
+ width: 125px;
+}
+
+/* BLOG FEEDS */
+.blog {
+ border: 10px solid #e5ecf9;
+ border-top: 1px solid #3366cc;
+}
+
+.blog h2 {
+ margin-top: 0.1em;
+ background-color: #e5ecf9;
+}
+
+.blog h2 a {
+ text-decoration: none;
+ color: black;
+}
+
+.blog h2 a:visited {
+ text-decoration: none;
+ color: black;
+}
+
+
+.blog .entry {
+ margin-bottom: 1em;
+}
+
+.blog .title {
+ font-size: medium;
+}
+
+.blog .author {
+ color: gray;
+ margin-bottom: 0.5em;
+}
+
+.blog .snippet {
+ background-color: white;
+}
+
+/* LIST */
+.list {
+ background-color: #E5ECF9;
+ border: 1px solid #93b7fa;
+ border-bottom: 2px solid #93b7fa;
+ padding: 3px;
+ -moz-border-radius: 5px 5px 0px 0px;
+}
+
+.list .pagination {
+ text-align: right;
+ padding: 3px;
+}
+
+.list table{
+ background-color: white;
+}
+
+.list table th {
+ background-color: #eeeeec;
+ border-right: 1px solid lightgray;
+ border-top: 1px solid lightgray;
+}
+
+.list table tr.on {
+ background-color: #ff9;
+}
+
+.list table tr.off {
+ background-color: #fff;
+}
+
+.list table td.last {
+ border-right: 1px solid lightgray;
+}
+
+.list table .first {
+ border-left: 1px solid lightgray;
+}
+
+/*
+ * CUSTOM CLASSES
+ */
+
+.todo {
+ color: #cc0000;
+ font-size: 80%;
+}
+
+.newmark {
+ color: red;
+ font-size: 80%;
+ vertical-align: top;
+}
+
+.error {
+ color: red;
+}
+
+.notice {
+ background:#fad163;
+ font-size: small;
+ font-weight: bold;
+}
+
+.rounded_ul { background: url(/soc/content/images/ul.gif) no-repeat top left; }
+.rounded_ur { background: url(/soc/content/images/ur.gif) no-repeat top right; }
+.rounded_ll { background: url(/soc/content/images/ll.gif) no-repeat bottom left; }
+.rounded_lr { background: url(/soc/content/images/lr.gif) no-repeat bottom right; }
+
+/* Disabled text. */
+.disabled {
+ color: gray;
+}
+
+
--- a/app/soc/templates/soc/base.html Mon Nov 10 23:18:06 2008 +0000
+++ b/app/soc/templates/soc/base.html Tue Nov 11 01:02:56 2008 +0000
@@ -16,7 +16,7 @@
<head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
{% block stylesheet %}
- <link rel="stylesheet" href="/soc/content/css/soc-081108.css" type="text/css"/>
+ <link rel="stylesheet" href="/soc/content/css/soc-081111.css" type="text/css"/>
{% endblock %}
<title>
{% block page_title %}
--- a/app/soc/templates/soc/templatetags/_field_as_table_row.html Mon Nov 10 23:18:06 2008 +0000
+++ b/app/soc/templates/soc/templatetags/_field_as_table_row.html Tue Nov 11 01:02:56 2008 +0000
@@ -28,12 +28,12 @@
<label for="{{ field.auto_id }}">{{ field.label }}:
</label>
</td>
+ <td>
+ {{ field }}
+ </td>
<td class="formfieldrequired">
{% if field.field.required %}(required){% endif %}
</td>
- <td>
- {{ field }}
- </td>
</tr>
--- a/app/soc/templates/soc/templatetags/_readonly_field_as_table_row.html Mon Nov 10 23:18:06 2008 +0000
+++ b/app/soc/templates/soc/templatetags/_readonly_field_as_table_row.html Tue Nov 11 01:02:56 2008 +0000
@@ -15,7 +15,6 @@
<td class="formfieldlabel">
{{ field_label }}:
</td>
- <td> </td>
<td class="formfieldvalue">
{{ field_value }}
</td>
--- a/app/soc/views/helper/forms.py Mon Nov 10 23:18:06 2008 +0000
+++ b/app/soc/views/helper/forms.py Tue Nov 11 01:02:56 2008 +0000
@@ -20,13 +20,38 @@
__authors__ = [
'"Chen Lunpeng" <forever.clp@gmail.com>',
'"Todd Larsen" <tlarsen@google.com>',
+ '"Pawel Solyga" <pawel.solyga@gmail.com>',
]
from google.appengine.ext.db import djangoforms
from django import forms
+from django.forms import forms as forms_in
+from django.forms import util
+from django.utils import encoding
from django.utils import safestring
+from django.utils.encoding import force_unicode
+from django.utils.html import escape
+from django.utils.safestring import mark_safe
+
+
+class CustomErrorList(util.ErrorList):
+ """A collection of errors that knows how to display itself in various formats.
+
+ This class has customized as_text method output which puts errors inside <span>
+ with formfielderrorlabel class.
+ """
+ def __unicode__(self):
+ return self.as_text()
+
+ def as_text(self):
+ """Returns error list rendered as text inside <span>."""
+ if not self:
+ return u''
+ errors_text = u'\n'.join([u'%s' % encoding.force_unicode(e) for e in self])
+ return u'<span class="formfielderrorlabel">%(errors)s</span><br />' % \
+ {'errors': errors_text}
class DbModelForm(djangoforms.ModelForm):
@@ -82,13 +107,14 @@
prints itself also has changed. Help text is displayed in the same row as
label and input.
"""
- # TODO(pawel.solyga): Add class names for form errors and required fields.
- DEF_NORMAL_ROW = u'<tr><td class="formfieldlabel">%(label)s</td>' \
- '<td>%(errors)s%(field)s%(help_text)s</td></tr>'
- DEF_ERROR_ROW = u'<tr><td colspan="2">%s</td></tr>'
+ DEF_NORMAL_ROW = u'<tr title="%(help_text)s"><td class=' \
+ '"%(field_class_type)s">%(label)s</td><td>' \
+ '%(errors)s%(field)s%(required)s</td></tr>'
+ DEF_ERROR_ROW = u'<tr><td> </td><td class="formfielderror">%s</td></tr>'
DEF_ROW_ENDER = '</td></tr>'
- DEF_HELP_TEXT_HTML = u'<td class="formfieldhelptext">%s</td>'
+ DEF_REQUIRED_HTML = u'<td class="formfieldrequired">(required)</td>'
+ DEF_HELP_TEXT_HTML = u'%s'
def __init__(self, *args, **kwargs):
"""Parent class initialization.
@@ -96,14 +122,98 @@
Args:
*args, **kwargs: passed through to parent __init__() constructor
"""
- super(BaseForm, self).__init__(*args, **kwargs)
+ super(BaseForm, self).__init__(error_class=CustomErrorList, *args, **kwargs)
+
+ def _html_output_with_required(self, normal_row, error_row, row_ender,
+ help_text_html, required_html, errors_on_separate_row):
+ """Helper function for outputting HTML.
+
+ Used by as_table(), as_ul(), as_p(). Displays information
+ about required fields.
+ """
+ # Errors that should be displayed above all fields.
+ top_errors = self.non_field_errors()
+ output, hidden_fields = [], []
+ for name, field in self.fields.items():
+ bf = forms_in.BoundField(self, field, name)
+ # Escape and cache in local variable.
+ bf_errors = self.error_class([escape(error) for error in bf.errors])
+ if bf.is_hidden:
+ if bf_errors:
+ top_errors.extend([u'(Hidden field %s) %s' % \
+ (name, force_unicode(e)) for e in bf_errors])
+ hidden_fields.append(unicode(bf))
+ else:
+ if errors_on_separate_row and bf_errors:
+ output.append(error_row % force_unicode(bf_errors))
+
+ if bf.label:
+ label = escape(force_unicode(bf.label))
+ # Only add the suffix if the label does not end in
+ # punctuation.
+ if self.label_suffix:
+ if label[-1] not in ':?.!':
+ label += self.label_suffix
+ 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''
+
+ if bf_errors:
+ field_class_type = u'formfielderrorlabel'
+ else:
+ field_class_type = u'formfieldlabel'
+
+ if field.required:
+ required = required_html
+ else:
+ required = u''
+
+ if errors_on_separate_row and bf_errors:
+ errors = u''
+ else:
+ errors = force_unicode(bf_errors)
+
+ output.append(normal_row % {'field_class_type': field_class_type,
+ 'errors': errors,
+ 'label': force_unicode(label),
+ 'field': unicode(bf),
+ 'required': required,
+ 'help_text': help_text})
+ 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:
+ last_row = output[-1]
+ # Chop off the trailing row_ender (e.g. '</td></tr>') and
+ # insert the hidden fields.
+ if not last_row.endswith(row_ender):
+ # This can happen in the as_p() case (and possibly others
+ # 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': ''}
+ output.append(last_row)
+ output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
+ else:
+ # If there aren't any rows in the output, just append the
+ # hidden fields.
+ output.append(str_hidden)
+ return mark_safe(u'\n'.join(output))
def as_table(self):
"""Returns form rendered as HTML <tr> rows -- with no <table></table>."""
- return self._html_output(self.DEF_NORMAL_ROW,
- self.DEF_ERROR_ROW,
- self.DEF_ROW_ENDER,
- self.DEF_HELP_TEXT_HTML, False)
+
+ return self._html_output_with_required(self.DEF_NORMAL_ROW,
+ self.DEF_ERROR_ROW,
+ self.DEF_ROW_ENDER,
+ self.DEF_HELP_TEXT_HTML,
+ self.DEF_REQUIRED_HTML, True)
class SelectQueryArgForm(forms.Form):