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 |
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> </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 """ |