web/lib/django/forms/forms.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 """
     1 """
     2 Form classes
     2 Form classes
     3 """
     3 """
     4 
     4 
     5 from copy import deepcopy
     5 from django.core.exceptions import ValidationError
     6 
     6 from django.utils.copycompat import deepcopy
     7 from django.utils.datastructures import SortedDict
     7 from django.utils.datastructures import SortedDict
     8 from django.utils.html import conditional_escape
     8 from django.utils.html import conditional_escape
     9 from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
     9 from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
    10 from django.utils.safestring import mark_safe
    10 from django.utils.safestring import mark_safe
    11 
    11 
    12 from fields import Field, FileField
    12 from fields import Field, FileField
    13 from widgets import Media, media_property, TextInput, Textarea
    13 from widgets import Media, media_property, TextInput, Textarea
    14 from util import flatatt, ErrorDict, ErrorList, ValidationError
    14 from util import flatatt, ErrorDict, ErrorList
    15 
    15 
    16 __all__ = ('BaseForm', 'Form')
    16 __all__ = ('BaseForm', 'Form')
    17 
    17 
    18 NON_FIELD_ERRORS = '__all__'
    18 NON_FIELD_ERRORS = '__all__'
    19 
    19 
    20 def pretty_name(name):
    20 def pretty_name(name):
    21     "Converts 'first_name' to 'First name'"
    21     """Converts 'first_name' to 'First name'""" 
    22     name = name[0].upper() + name[1:]
    22     if not name: 
    23     return name.replace('_', ' ')
    23         return u'' 
       
    24     return name.replace('_', ' ').capitalize() 
    24 
    25 
    25 def get_declared_fields(bases, attrs, with_base_fields=True):
    26 def get_declared_fields(bases, attrs, with_base_fields=True):
    26     """
    27     """
    27     Create a list of form field instances from the passed in 'attrs', plus any
    28     Create a list of form field instances from the passed in 'attrs', plus any
    28     similar fields on the base classes (in 'bases'). This is used by both the
    29     similar fields on the base classes (in 'bases'). This is used by both the
   136 
   137 
   137     def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
   138     def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
   138         "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
   139         "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
   139         top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
   140         top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
   140         output, hidden_fields = [], []
   141         output, hidden_fields = [], []
       
   142 
   141         for name, field in self.fields.items():
   143         for name, field in self.fields.items():
       
   144             html_class_attr = ''
   142             bf = BoundField(self, field, name)
   145             bf = BoundField(self, field, name)
   143             bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable.
   146             bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable.
   144             if bf.is_hidden:
   147             if bf.is_hidden:
   145                 if bf_errors:
   148                 if bf_errors:
   146                     top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
   149                     top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
   147                 hidden_fields.append(unicode(bf))
   150                 hidden_fields.append(unicode(bf))
   148             else:
   151             else:
       
   152                 # Create a 'class="..."' atribute if the row should have any
       
   153                 # CSS classes applied.
       
   154                 css_classes = bf.css_classes()
       
   155                 if css_classes:
       
   156                     html_class_attr = ' class="%s"' % css_classes
       
   157 
   149                 if errors_on_separate_row and bf_errors:
   158                 if errors_on_separate_row and bf_errors:
   150                     output.append(error_row % force_unicode(bf_errors))
   159                     output.append(error_row % force_unicode(bf_errors))
       
   160 
   151                 if bf.label:
   161                 if bf.label:
   152                     label = conditional_escape(force_unicode(bf.label))
   162                     label = conditional_escape(force_unicode(bf.label))
   153                     # Only add the suffix if the label does not end in
   163                     # Only add the suffix if the label does not end in
   154                     # punctuation.
   164                     # punctuation.
   155                     if self.label_suffix:
   165                     if self.label_suffix:
   156                         if label[-1] not in ':?.!':
   166                         if label[-1] not in ':?.!':
   157                             label += self.label_suffix
   167                             label += self.label_suffix
   158                     label = bf.label_tag(label) or ''
   168                     label = bf.label_tag(label) or ''
   159                 else:
   169                 else:
   160                     label = ''
   170                     label = ''
       
   171 
   161                 if field.help_text:
   172                 if field.help_text:
   162                     help_text = help_text_html % force_unicode(field.help_text)
   173                     help_text = help_text_html % force_unicode(field.help_text)
   163                 else:
   174                 else:
   164                     help_text = u''
   175                     help_text = u''
   165                 output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text})
   176 
       
   177                 output.append(normal_row % {
       
   178                     'errors': force_unicode(bf_errors),
       
   179                     'label': force_unicode(label),
       
   180                     'field': unicode(bf),
       
   181                     'help_text': help_text,
       
   182                     'html_class_attr': html_class_attr
       
   183                 })
       
   184 
   166         if top_errors:
   185         if top_errors:
   167             output.insert(0, error_row % force_unicode(top_errors))
   186             output.insert(0, error_row % force_unicode(top_errors))
       
   187 
   168         if hidden_fields: # Insert any hidden fields in the last row.
   188         if hidden_fields: # Insert any hidden fields in the last row.
   169             str_hidden = u''.join(hidden_fields)
   189             str_hidden = u''.join(hidden_fields)
   170             if output:
   190             if output:
   171                 last_row = output[-1]
   191                 last_row = output[-1]
   172                 # Chop off the trailing row_ender (e.g. '</td></tr>') and
   192                 # Chop off the trailing row_ender (e.g. '</td></tr>') and
   174                 if not last_row.endswith(row_ender):
   194                 if not last_row.endswith(row_ender):
   175                     # This can happen in the as_p() case (and possibly others
   195                     # This can happen in the as_p() case (and possibly others
   176                     # that users write): if there are only top errors, we may
   196                     # that users write): if there are only top errors, we may
   177                     # not be able to conscript the last row for our purposes,
   197                     # not be able to conscript the last row for our purposes,
   178                     # so insert a new, empty row.
   198                     # so insert a new, empty row.
   179                     last_row = normal_row % {'errors': '', 'label': '', 'field': '', 'help_text': ''}
   199                     last_row = (normal_row % {'errors': '', 'label': '',
       
   200                                               'field': '', 'help_text':'',
       
   201                                               'html_class_attr': html_class_attr})
   180                     output.append(last_row)
   202                     output.append(last_row)
   181                 output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
   203                 output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
   182             else:
   204             else:
   183                 # If there aren't any rows in the output, just append the
   205                 # If there aren't any rows in the output, just append the
   184                 # hidden fields.
   206                 # hidden fields.
   185                 output.append(str_hidden)
   207                 output.append(str_hidden)
   186         return mark_safe(u'\n'.join(output))
   208         return mark_safe(u'\n'.join(output))
   187 
   209 
   188     def as_table(self):
   210     def as_table(self):
   189         "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
   211         "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
   190         return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
   212         return self._html_output(
       
   213             normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>',
       
   214             error_row = u'<tr><td colspan="2">%s</td></tr>',
       
   215             row_ender = u'</td></tr>',
       
   216             help_text_html = u'<br />%s',
       
   217             errors_on_separate_row = False)
   191 
   218 
   192     def as_ul(self):
   219     def as_ul(self):
   193         "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
   220         "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
   194         return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
   221         return self._html_output(
       
   222             normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>',
       
   223             error_row = u'<li>%s</li>',
       
   224             row_ender = '</li>',
       
   225             help_text_html = u' %s',
       
   226             errors_on_separate_row = False)
   195 
   227 
   196     def as_p(self):
   228     def as_p(self):
   197         "Returns this form rendered as HTML <p>s."
   229         "Returns this form rendered as HTML <p>s."
   198         return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True)
   230         return self._html_output(
       
   231             normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>',
       
   232             error_row = u'%s',
       
   233             row_ender = '</p>',
       
   234             help_text_html = u' %s',
       
   235             errors_on_separate_row = True)
   199 
   236 
   200     def non_field_errors(self):
   237     def non_field_errors(self):
   201         """
   238         """
   202         Returns an ErrorList of errors that aren't associated with a particular
   239         Returns an ErrorList of errors that aren't associated with a particular
   203         field -- i.e., from Form.clean(). Returns an empty ErrorList if there
   240         field -- i.e., from Form.clean(). Returns an empty ErrorList if there
   225         self.cleaned_data = {}
   262         self.cleaned_data = {}
   226         # If the form is permitted to be empty, and none of the form data has
   263         # If the form is permitted to be empty, and none of the form data has
   227         # changed from the initial data, short circuit any validation.
   264         # changed from the initial data, short circuit any validation.
   228         if self.empty_permitted and not self.has_changed():
   265         if self.empty_permitted and not self.has_changed():
   229             return
   266             return
       
   267         self._clean_fields()
       
   268         self._clean_form()
       
   269         self._post_clean()
       
   270         if self._errors:
       
   271             delattr(self, 'cleaned_data')
       
   272 
       
   273     def _clean_fields(self):
   230         for name, field in self.fields.items():
   274         for name, field in self.fields.items():
   231             # value_from_datadict() gets the data from the data dictionaries.
   275             # value_from_datadict() gets the data from the data dictionaries.
   232             # Each widget type knows how to retrieve its own data, because some
   276             # Each widget type knows how to retrieve its own data, because some
   233             # widgets split data over several HTML fields.
   277             # widgets split data over several HTML fields.
   234             value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
   278             value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
   244                     self.cleaned_data[name] = value
   288                     self.cleaned_data[name] = value
   245             except ValidationError, e:
   289             except ValidationError, e:
   246                 self._errors[name] = self.error_class(e.messages)
   290                 self._errors[name] = self.error_class(e.messages)
   247                 if name in self.cleaned_data:
   291                 if name in self.cleaned_data:
   248                     del self.cleaned_data[name]
   292                     del self.cleaned_data[name]
       
   293 
       
   294     def _clean_form(self):
   249         try:
   295         try:
   250             self.cleaned_data = self.clean()
   296             self.cleaned_data = self.clean()
   251         except ValidationError, e:
   297         except ValidationError, e:
   252             self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
   298             self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
   253         if self._errors:
   299 
   254             delattr(self, 'cleaned_data')
   300     def _post_clean(self):
       
   301         """
       
   302         An internal hook for performing additional cleaning after form cleaning
       
   303         is complete. Used for model validation in model forms.
       
   304         """
       
   305         pass
   255 
   306 
   256     def clean(self):
   307     def clean(self):
   257         """
   308         """
   258         Hook for doing any extra form-wide cleaning after Field.clean() been
   309         Hook for doing any extra form-wide cleaning after Field.clean() been
   259         called on every field. Any ValidationError raised by this method will
   310         called on every field. Any ValidationError raised by this method will
   341         self.form = form
   392         self.form = form
   342         self.field = field
   393         self.field = field
   343         self.name = name
   394         self.name = name
   344         self.html_name = form.add_prefix(name)
   395         self.html_name = form.add_prefix(name)
   345         self.html_initial_name = form.add_initial_prefix(name)
   396         self.html_initial_name = form.add_initial_prefix(name)
       
   397         self.html_initial_id = form.add_initial_prefix(self.auto_id)
   346         if self.field.label is None:
   398         if self.field.label is None:
   347             self.label = pretty_name(name)
   399             self.label = pretty_name(name)
   348         else:
   400         else:
   349             self.label = self.field.label
   401             self.label = self.field.label
   350         self.help_text = field.help_text or ''
   402         self.help_text = field.help_text or ''
   372         if not widget:
   424         if not widget:
   373             widget = self.field.widget
   425             widget = self.field.widget
   374         attrs = attrs or {}
   426         attrs = attrs or {}
   375         auto_id = self.auto_id
   427         auto_id = self.auto_id
   376         if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
   428         if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
   377             attrs['id'] = auto_id
   429             if not only_initial:
       
   430                 attrs['id'] = auto_id
       
   431             else:
       
   432                 attrs['id'] = self.html_initial_id
   378         if not self.form.is_bound:
   433         if not self.form.is_bound:
   379             data = self.form.initial.get(self.name, self.field.initial)
   434             data = self.form.initial.get(self.name, self.field.initial)
   380             if callable(data):
   435             if callable(data):
   381                 data = data()
   436                 data = data()
   382         else:
   437         else:
   386                 data = self.data
   441                 data = self.data
   387         if not only_initial:
   442         if not only_initial:
   388             name = self.html_name
   443             name = self.html_name
   389         else:
   444         else:
   390             name = self.html_initial_name
   445             name = self.html_initial_name
       
   446         if self.field.localize:
       
   447             data = self.field.localize_value(data)
   391         return widget.render(name, data, attrs=attrs)
   448         return widget.render(name, data, attrs=attrs)
   392 
   449 
   393     def as_text(self, attrs=None, **kwargs):
   450     def as_text(self, attrs=None, **kwargs):
   394         """
   451         """
   395         Returns a string of HTML for representing this as an <input type="text">.
   452         Returns a string of HTML for representing this as an <input type="text">.
   427         if id_:
   484         if id_:
   428             attrs = attrs and flatatt(attrs) or ''
   485             attrs = attrs and flatatt(attrs) or ''
   429             contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents))
   486             contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents))
   430         return mark_safe(contents)
   487         return mark_safe(contents)
   431 
   488 
       
   489     def css_classes(self, extra_classes=None):
       
   490         """
       
   491         Returns a string of space-separated CSS classes for this field.
       
   492         """
       
   493         if hasattr(extra_classes, 'split'):
       
   494             extra_classes = extra_classes.split()
       
   495         extra_classes = set(extra_classes or [])
       
   496         if self.errors and hasattr(self.form, 'error_css_class'):
       
   497             extra_classes.add(self.form.error_css_class)
       
   498         if self.field.required and hasattr(self.form, 'required_css_class'):
       
   499             extra_classes.add(self.form.required_css_class)
       
   500         return ' '.join(extra_classes)
       
   501 
   432     def _is_hidden(self):
   502     def _is_hidden(self):
   433         "Returns True if this BoundField's widget is hidden."
   503         "Returns True if this BoundField's widget is hidden."
   434         return self.field.widget.is_hidden
   504         return self.field.widget.is_hidden
   435     is_hidden = property(_is_hidden)
   505     is_hidden = property(_is_hidden)
   436 
   506