Add BaseForm class to soc.views.helper.forms module (work in progress). This changes...
[Melange.git] / app / soc / views / helper / forms.py
blobfa14dfd048d2cfa9d6f45228168d164e74757488
1 #!/usr/bin/python2.5
3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Helpers used to display various views that are forms.
18 """
20 __authors__ = [
21 '"Chen Lunpeng" <forever.clp@gmail.com>',
22 '"Todd Larsen" <tlarsen@google.com>',
26 from google.appengine.ext.db import djangoforms
28 from django import forms
29 from django.utils import safestring
32 class DbModelForm(djangoforms.ModelForm):
33 """Subclass of Django ModelForm that fixes some label and help_text issues.
35 The default behavior of ModelForm is to use the verbose_name in all
36 lowercase, capitalizing only the first character, as the displayed field
37 label. This class uses verbose_name unaltered as the visible field label
38 instead.
40 The Property classes used by the App Engine Datastore do not have a
41 help_text parameter to their constructor. In a Model class, a help_text
42 attribute *can* be added to the property after it is created, but the
43 help text will not be automatically passed along to the Django ModelForm.
44 This class detects the presence of a help_text attribute and adds it to
45 the corresponding form field object.
47 ugettext_lazy() proxies used for internationalization in the Model will
48 still work correctly with this new behavior, as long as the original
49 strings are used as the translation keys.
50 """
52 def __init__(self, *args, **kwargs):
53 """Fixes label and help_text issues after parent initialization.
55 Args:
56 *args, **kwargs: passed through to parent __init__() constructor
57 """
58 super(DbModelForm, self).__init__(*args, **kwargs)
60 for field_name in self.fields.iterkeys():
61 # Since fields can be added only to the ModelForm subclass, check to
62 # see if the Model has a corresponding field first.
63 if hasattr(self.Meta.model, field_name):
64 model_prop = getattr(self.Meta.model, field_name)
66 # Check if the Model property defined verbose_name, and copy that
67 # verbatim to the corresponding field label.
68 if hasattr(model_prop, 'verbose_name'):
69 self.fields[field_name].label = model_prop.verbose_name
71 # Check if the Model property added help_text, and copy that verbatim
72 # to the corresponding field help_text.
73 if hasattr(model_prop, 'help_text'):
74 self.fields[field_name].help_text = model_prop.help_text
77 class BaseForm(DbModelForm):
78 """Subclass of DbModelForm that extends as_table HTML output.
80 BaseForm has additional class names in HTML tags for label and help text
81 and those can be used in CSS files for look customization. The way the Form
82 prints itself also has changed. Help text is displayed in the same row as
83 label and input.
84 """
85 # TODO(pawel.solyga): Add class names for form errors and required fields.
87 DEF_NORMAL_ROW = u'<tr><td class="formfieldlabel">%(label)s</td>' \
88 '<td>%(errors)s%(field)s%(help_text)s</td></tr>'
89 DEF_ERROR_ROW = u'<tr><td colspan="2">%s</td></tr>'
90 DEF_ROW_ENDER = '</td></tr>'
91 DEF_HELP_TEXT_HTML = u'<td class="formfieldhelptext">%s</td>'
93 def __init__(self, *args, **kwargs):
94 """Parent class initialization.
96 Args:
97 *args, **kwargs: passed through to parent __init__() constructor
98 """
99 super(BaseForm, self).__init__(*args, **kwargs)
101 def as_table(self):
102 """Returns form rendered as HTML <tr> rows -- with no <table></table>."""
103 return self._html_output(self.DEF_NORMAL_ROW,
104 self.DEF_ERROR_ROW,
105 self.DEF_ROW_ENDER,
106 self.DEF_HELP_TEXT_HTML, False)
109 class SelectQueryArgForm(forms.Form):
110 """URL query argument change control implemented as a Django form.
113 ONCHANGE_JAVASCRIPT_FMT = '''
114 <script type="text/javascript">
115 function changeArg_%(arg_name)s(item)
117 var idx=item.selectedIndex;
118 item.selected=true;
119 var value=item.value
120 var url = location.href
121 var reg = /%(arg_name)s=\d+/
122 url = url.replace(reg, "%(arg_name)s="+value)
123 if(url.match(reg))
124 document.location.href = url
125 else
126 document.location.href = "%(page_path)s?%(arg_name)s="+value;
128 </script>
131 def __init__(self, page_path, arg_name, choices, field_name,
132 *form_args, **form_kwargs):
134 Args:
135 page_path: (usually request.path)
136 arg_name: the URL query parameter that determines which choice is
137 selected in the selection control
138 choices: list (or tuple) of value/label string two-tuples, for example:
139 (('10', '10 items per page'), ('25', '25 items per page'))
140 field_name: name of the selection field in the form
141 *form_args: positional arguments passed on to the Form base
142 class __init__()
143 *form_kwargs: keyword arguments passed on to the Form base
144 class __init__()
146 super(SelectQueryArgForm, self).__init__(*form_args, **form_kwargs)
148 self._script = safestring.mark_safe(self.ONCHANGE_JAVASCRIPT_FMT % {
149 'arg_name': arg_name, 'page_path': page_path,})
151 onchange_js_call = 'changeArg_%s(this)' % arg_name
153 self.fields[field_name] = forms.ChoiceField(
154 label='', choices=choices,
155 widget=forms.widgets.Select(attrs={'onchange': onchange_js_call}))
157 def as_table(self):
158 """Returns form rendered as HTML <tr> rows -- with no <table></table>.
160 Prepends <script> section with onchange function included.
162 return self._script + super(SelectQueryArgForm, self).as_table()
164 def as_ul(self):
165 """Returns form rendered as HTML <li> list items -- with no <ul></ul>.
167 Prepends <script> section with onchange function included.
169 return self._script + super(SelectQueryArgForm, self).as_ul()
171 def as_p(self):
172 """Returns form rendered as HTML <p> paragraphs.
174 Prepends <script> section with onchange function included.
176 return self._script + super(SelectQueryArgForm, self).as_p()
179 DEF_SELECT_QUERY_ARG_FIELD_NAME_FMT = 'select_query_arg_%(arg_name)s'
181 def makeSelectQueryArgForm(
182 request, arg_name, initial_value, choices,
183 field_name_fmt=DEF_SELECT_QUERY_ARG_FIELD_NAME_FMT):
184 """Wrapper that creates a customized SelectQueryArgForm.
186 Args:
187 request: the standard Django HTTP request object
188 arg_name: the URL query parameter that determines which choice is
189 selected in the selection control
190 initial_value: the initial value of the selection control
191 choices: list (or tuple) of value/label string two-tuples, for example:
192 (('10', '10 items per page'), ('25', '25 items per page'))
193 field_name_fmt: optional form field name format string; default is
194 DEF_SELECT_QUERY_ARG_FIELD_NAME_FMT; contains these named format
195 specifiers:
196 arg_name: replaced with the arg_name argument
198 Returns:
199 a Django form implementing a query argument selection control, for
200 insertion into a template
202 field_name = field_name_fmt % {'arg_name': arg_name}
203 return SelectQueryArgForm(request.path, arg_name, choices, field_name,
204 initial={field_name: initial_value})