app/soc/views/helper/forms.py
changeset 741 2dc2c65c5f76
parent 739 b245c2b814c5
child 753 7d6dead86b59
equal deleted inserted replaced
740:caa143c799a7 741:2dc2c65c5f76
    25 
    25 
    26 
    26 
    27 from google.appengine.ext.db import djangoforms
    27 from google.appengine.ext.db import djangoforms
    28 
    28 
    29 from django import forms
    29 from django import forms
    30 from django.forms import forms as forms_in
       
    31 from django.forms import util
       
    32 from django.utils import encoding
       
    33 from django.utils import safestring
    30 from django.utils import safestring
    34 from django.utils.encoding import force_unicode
       
    35 from django.utils.html import escape
       
    36 from django.utils.safestring import mark_safe
    31 from django.utils.safestring import mark_safe
    37 
    32 
    38 
    33 
    39 class CustomErrorList(util.ErrorList):
    34 class BaseForm(djangoforms.ModelForm):
    40   """A collection of errors that knows how to display itself in various formats.
       
    41   
       
    42   This class has customized as_text method output which puts errors inside <span>
       
    43   with formfielderrorlabel class.
       
    44   """
       
    45   def __unicode__(self):
       
    46     return self.as_text()
       
    47   
       
    48   def as_text(self):
       
    49     """Returns error list rendered as text inside <span>."""
       
    50     if not self:
       
    51       return u''
       
    52     errors_text = u'\n'.join([u'%s' % encoding.force_unicode(e) for e in self])
       
    53     return u'<span class="formfielderrorlabel">%(errors)s</span><br />' % \
       
    54         {'errors': errors_text}
       
    55 
       
    56 
       
    57 class DbModelForm(djangoforms.ModelForm):
       
    58   """Subclass of Django ModelForm that fixes some label and help_text issues.
    35   """Subclass of Django ModelForm that fixes some label and help_text issues.
    59 
    36 
    60   The default behavior of ModelForm is to use the verbose_name in all
    37   The default behavior of ModelForm is to use the verbose_name in all
    61   lowercase, capitalizing only the first character, as the displayed field
    38   lowercase, capitalizing only the first character, as the displayed field
    62   label.  This class uses verbose_name unaltered as the visible field label
    39   label.  This class uses verbose_name unaltered as the visible field label
    78     """Fixes label and help_text issues after parent initialization.
    55     """Fixes label and help_text issues after parent initialization.
    79 
    56 
    80     Args:
    57     Args:
    81       *args, **kwargs:  passed through to parent __init__() constructor
    58       *args, **kwargs:  passed through to parent __init__() constructor
    82     """
    59     """
    83     super(DbModelForm, self).__init__(*args, **kwargs)
    60     super(BaseForm, self).__init__(*args, **kwargs)
    84 
    61 
    85     for field_name in self.fields.iterkeys():
    62     for field_name in self.fields.iterkeys():
    86       # Since fields can be added only to the ModelForm subclass, check to
    63       # Since fields can be added only to the ModelForm subclass, check to
    87       # see if the Model has a corresponding field first.
    64       # see if the Model has a corresponding field first.
    88       if hasattr(self.Meta.model, field_name):
    65       if hasattr(self.Meta.model, field_name):
   100 
    77 
   101         # Check if the Model property added example_text, and copy that verbatim
    78         # Check if the Model property added example_text, and copy that verbatim
   102         # to the corresponding field help_text.
    79         # to the corresponding field help_text.
   103         if hasattr(model_prop, 'example_text'):
    80         if hasattr(model_prop, 'example_text'):
   104           self.fields[field_name].example_text = model_prop.example_text
    81           self.fields[field_name].example_text = model_prop.example_text
   105 
       
   106 class BaseForm(DbModelForm):
       
   107   """Subclass of DbModelForm that extends as_table HTML output.
       
   108   
       
   109   BaseForm has additional class names in HTML tags for label and help text
       
   110   and those can be used in CSS files for look customization. The way the Form
       
   111   prints itself also has changed. Help text is displayed in the same row as 
       
   112   label and input.
       
   113   """
       
   114   
       
   115   DEF_NORMAL_ROW = u'<tr title="%(help_text)s"><td class=' \
       
   116       '"%(field_class_type)s">%(label)s</td><td>' \
       
   117       '%(errors)s%(field)s%(required)s%(example_text)s</td></tr>'
       
   118   DEF_ERROR_ROW = u'<tr><td>&nbsp;</td><td class="formfielderror">%s</td></tr>'
       
   119   DEF_ROW_ENDER = '</td></tr>'
       
   120   DEF_REQUIRED_HTML = u'<td class="formfieldrequired">(required)</td>'
       
   121   DEF_HELP_TEXT_HTML = u'%s'
       
   122   DEF_EXAMPLE_TEXT_HTML = u'<td class="formfieldexample">e.g. %s</td>'
       
   123 
       
   124 
       
   125   def __init__(self, *args, **kwargs):
       
   126     """Parent class initialization.
       
   127 
       
   128     Args:
       
   129       *args, **kwargs:  passed through to parent __init__() constructor
       
   130     """
       
   131     super(BaseForm, self).__init__(error_class=CustomErrorList, *args, **kwargs)
       
   132 
       
   133   def _html_output_with_required(self, normal_row, error_row, row_ender,
       
   134           help_text_html, required_html, example_text_html, errors_on_separate_row):
       
   135     """Helper function for outputting HTML.
       
   136 
       
   137     Used by as_table(), as_ul(), as_p(). Displays information
       
   138     about required fields.
       
   139     """
       
   140     # Errors that should be displayed above all fields.
       
   141     top_errors = self.non_field_errors()
       
   142     output, hidden_fields = [], []
       
   143     for name, field in self.fields.items():
       
   144       bf = forms_in.BoundField(self, field, name)
       
   145       # Escape and cache in local variable.
       
   146       bf_errors = self.error_class([escape(error) for error in bf.errors])
       
   147       if bf.is_hidden:
       
   148         if bf_errors:
       
   149           top_errors.extend([u'(Hidden field %s) %s' % \
       
   150               (name, force_unicode(e)) for e in bf_errors])
       
   151         hidden_fields.append(unicode(bf))
       
   152       else:
       
   153         if errors_on_separate_row and bf_errors:
       
   154           output.append(error_row % force_unicode(bf_errors))
       
   155 
       
   156         if bf.label:
       
   157           label = escape(force_unicode(bf.label))
       
   158           # Only add the suffix if the label does not end in
       
   159           # punctuation.
       
   160           if self.label_suffix:
       
   161             if label[-1] not in ':?.!':
       
   162               label += self.label_suffix
       
   163           label = bf.label_tag(label) or ''
       
   164         else:
       
   165           label = ''
       
   166         if field.help_text:
       
   167           help_text = help_text_html % force_unicode(field.help_text)
       
   168         else:
       
   169           help_text = u''
       
   170 
       
   171         if bf_errors:
       
   172           field_class_type = u'formfielderrorlabel'
       
   173         else:
       
   174           field_class_type = u'formfieldlabel'
       
   175 
       
   176         if field.required:
       
   177           required = required_html
       
   178         else:
       
   179           required = u'<td></td>'
       
   180 
       
   181         if hasattr(field, 'example_text'):
       
   182           example_text = example_text_html % force_unicode(field.example_text)
       
   183         else:
       
   184           example_text = u'<td></td>'
       
   185 
       
   186         if errors_on_separate_row and bf_errors:
       
   187           errors = u''
       
   188         else:
       
   189           errors = force_unicode(bf_errors)
       
   190 
       
   191         output.append(normal_row % {'field_class_type': field_class_type,
       
   192                                     'errors': errors,
       
   193                                     'label': force_unicode(label),
       
   194                                     'field': unicode(bf),
       
   195                                     'required': required,
       
   196                                     'help_text': help_text,
       
   197                                     'example_text': example_text})
       
   198     if top_errors:
       
   199       output.insert(0, error_row % force_unicode(top_errors))
       
   200     if hidden_fields: # Insert any hidden fields in the last row.
       
   201       str_hidden = u''.join(hidden_fields)
       
   202       if output:
       
   203         last_row = output[-1]
       
   204         # Chop off the trailing row_ender (e.g. '</td></tr>') and
       
   205         # insert the hidden fields.
       
   206         if not last_row.endswith(row_ender):
       
   207           # This can happen in the as_p() case (and possibly others
       
   208           # that users write): if there are only top errors, we may
       
   209           # not be able to conscript the last row for our purposes,
       
   210           # so insert a new, empty row.
       
   211           last_row = normal_row % {'errors': '', 'label': '',
       
   212                                    'field': '', 'help_text': '', 'example_text': ''}
       
   213           output.append(last_row)
       
   214         output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
       
   215       else:
       
   216         # If there aren't any rows in the output, just append the
       
   217         # hidden fields.
       
   218         output.append(str_hidden)
       
   219 
       
   220     return mark_safe(u'\n'.join(output))
       
   221 
       
   222   def as_table(self):
       
   223     """Returns form rendered as HTML <tr> rows -- with no <table></table>."""
       
   224 
       
   225     return self._html_output_with_required(self.DEF_NORMAL_ROW,
       
   226                                            self.DEF_ERROR_ROW,
       
   227                                            self.DEF_ROW_ENDER,
       
   228                                            self.DEF_HELP_TEXT_HTML,
       
   229                                            self.DEF_REQUIRED_HTML,
       
   230                                            self.DEF_EXAMPLE_TEXT_HTML, True)
       
   231 
    82 
   232 
    83 
   233 class SelectQueryArgForm(forms.Form):
    84 class SelectQueryArgForm(forms.Form):
   234   """URL query argument change control implemented as a Django form.
    85   """URL query argument change control implemented as a Django form.
   235   """
    86   """