# HG changeset patch # User Sverre Rabbelier # Date 1229214839 0 # Node ID 2dc2c65c5f76afbebd75ce48b3925eb5bb51f69a # Parent caa143c799a76c9146b289ff769fa947dd08dd50 Converted as_table to be a template tag This allows us to move all the HTML from python code (which made it extremely icky and unreadable) to Django templates, which is _a lot_ more readable, and makes it feasable for HTML/CSS experts to change the way these tables are generated. Patch by: Sverre Rabbelier diff -r caa143c799a7 -r 2dc2c65c5f76 app/soc/templates/soc/models/edit.html --- a/app/soc/templates/soc/models/edit.html Sun Dec 14 00:33:41 2008 +0000 +++ b/app/soc/templates/soc/models/edit.html Sun Dec 14 00:33:59 2008 +0000 @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. {% endcomment %} +{% load forms_helpers %} {% block scripts %} @@ -33,13 +34,13 @@ {% endblock %}

- - {{ form.as_table }} +
+ {% as_table form %} +
+ -
 
- {% block submit_buttons %} + + + +{% endif %} + + + + + {% else %} + + {% endif %} + + {% if example_text %} + + {% else %} + + {% endif %} + + diff -r caa143c799a7 -r 2dc2c65c5f76 app/soc/views/helper/forms.py --- a/app/soc/views/helper/forms.py Sun Dec 14 00:33:41 2008 +0000 +++ b/app/soc/views/helper/forms.py Sun Dec 14 00:33:59 2008 +0000 @@ -27,34 +27,11 @@ 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 - with formfielderrorlabel class. - """ - def __unicode__(self): - return self.as_text() - - def as_text(self): - """Returns error list rendered as text inside .""" - if not self: - return u'' - errors_text = u'\n'.join([u'%s' % encoding.force_unicode(e) for e in self]) - return u'%(errors)s
' % \ - {'errors': errors_text} - - -class DbModelForm(djangoforms.ModelForm): +class BaseForm(djangoforms.ModelForm): """Subclass of Django ModelForm that fixes some label and help_text issues. The default behavior of ModelForm is to use the verbose_name in all @@ -80,7 +57,7 @@ Args: *args, **kwargs: passed through to parent __init__() constructor """ - super(DbModelForm, self).__init__(*args, **kwargs) + super(BaseForm, self).__init__(*args, **kwargs) for field_name in self.fields.iterkeys(): # Since fields can be added only to the ModelForm subclass, check to @@ -103,132 +80,6 @@ if hasattr(model_prop, 'example_text'): self.fields[field_name].example_text = model_prop.example_text -class BaseForm(DbModelForm): - """Subclass of DbModelForm that extends as_table HTML output. - - BaseForm has additional class names in HTML tags for label and help text - and those can be used in CSS files for look customization. The way the Form - prints itself also has changed. Help text is displayed in the same row as - label and input. - """ - - DEF_NORMAL_ROW = u'
' - DEF_ERROR_ROW = u'' - DEF_ROW_ENDER = '' - DEF_REQUIRED_HTML = u'' - DEF_HELP_TEXT_HTML = u'%s' - DEF_EXAMPLE_TEXT_HTML = u'' - - - def __init__(self, *args, **kwargs): - """Parent class initialization. - - Args: - *args, **kwargs: passed through to parent __init__() constructor - """ - 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, example_text_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 hasattr(field, 'example_text'): - example_text = example_text_html % force_unicode(field.example_text) - else: - example_text = 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, - 'example_text': example_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. '') 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': '', 'example_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 rows -- with no
diff -r caa143c799a7 -r 2dc2c65c5f76 app/soc/templates/soc/templatetags/_as_table.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/soc/templates/soc/templatetags/_as_table.html Sun Dec 14 00:33:59 2008 +0000 @@ -0,0 +1,25 @@ +{% comment %} +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. +{% endcomment %} +{% load forms_helpers %} + +{{ top_errors }} +{{ hidden_field_errors }} + +{% for field, required, example_text in fields %} + {% as_table_row form field required example_text %} +{% endfor %} + +{% for field in hidden_fields %} + {{ field }} +{% endfor %} diff -r caa143c799a7 -r 2dc2c65c5f76 app/soc/templates/soc/templatetags/_as_table_row.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/soc/templates/soc/templatetags/_as_table_row.html Sun Dec 14 00:33:59 2008 +0000 @@ -0,0 +1,45 @@ +{% comment %} +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. +{% endcomment %} + +{% if errors %} +
  + {% for error in errors %} + {{ error }}
+ {% endfor %} +
+ {{ label }} + + {{ field }} + + {% if required %} + (required)e.g. {{ example_text }}
%(label)s' \ - '%(errors)s%(field)s%(required)s%(example_text)s
 %s
(required)e.g. %s
.""" - - 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, - self.DEF_EXAMPLE_TEXT_HTML, True) - class SelectQueryArgForm(forms.Form): """URL query argument change control implemented as a Django form. diff -r caa143c799a7 -r 2dc2c65c5f76 app/soc/views/helper/templatetags/forms_helpers.py --- a/app/soc/views/helper/templatetags/forms_helpers.py Sun Dec 14 00:33:41 2008 +0000 +++ b/app/soc/views/helper/templatetags/forms_helpers.py Sun Dec 14 00:33:59 2008 +0000 @@ -20,10 +20,15 @@ __authors__ = [ '"Todd Larsen" ', '"Pawel Solyga" ', + '"Sverre Rabbelier" ', ] from django import template +from django.forms import forms as forms_in +from django.utils import encoding +from django.utils.encoding import force_unicode +from django.utils.html import escape register = template.Library() @@ -97,3 +102,81 @@ """ return {'field_label': field_label, 'field_value': field_value} + + +@register.inclusion_tag('soc/templatetags/_as_table.html') +def as_table(form): + """Outputs a form as a properly formatted html table + + Args: + form: the form that should be converted to a table + """ + + fields = [] + hidden_fields = [] + hidden_fields_errors = [] + + # Iterate over all fields and prepare it for adding + for name, field in form.fields.items(): + bf = forms_in.BoundField(form, field, name) + + # If the field is hidden we display it elsewhere + if not bf.is_hidden: + example_text = '' + if hasattr(field, 'example_text'): + example_text = force_unicode(field.example_text) + + item = (bf, field.required, example_text) + fields.append(item) + else: + hidden_fields.append(unicode(bf)) + + for e in bf.errors: + item = (name, force_unicode(e)) + hidden_fields_errors.append(item) + + return { + 'top_errors': form.non_field_errors() or '', + 'hidden_field_errors': hidden_fields_errors or '', + 'form': form, + 'fields': fields or '', + 'hidden_fields': hidden_fields or '', + } + + +@register.inclusion_tag('soc/templatetags/_as_table_row.html') +def as_table_row(form, field, required, example_text): + """Outputs a field as a properly formatted html row + + Args: + form: the form that the row belongs to + field: the field that should be converted to a row + required: whether the field is required + example_text: the example_text for this row + """ + + # Escape and cache in local variable. + errors = [force_unicode(escape(error)) for error in field.errors] + + if field.label: + label = escape(force_unicode(field.label)) + + # Only add the suffix if the label does not end in punctuation. + if form.label_suffix and (label[-1] not in ':?.!'): + label += form.label_suffix + + label = field.label_tag(label) or '' + + field_class_type = 'formfield%slabel' % ('error' if errors else '') + + help_text = field.help_text + + return { + 'help_text': force_unicode(help_text) if help_text else '', + 'field_class_type': field_class_type, + 'label': force_unicode(label) if field.label else '', + 'field': unicode(field), + 'required': required, + 'example_text': example_text, + 'errors': errors, + }