diff options
| author | Boulder Sprinters <boulder-sprinters@djangoproject.com> | 2006-11-17 06:47:43 +0000 |
|---|---|---|
| committer | Boulder Sprinters <boulder-sprinters@djangoproject.com> | 2006-11-17 06:47:43 +0000 |
| commit | 8154294a0fb0134a13676642d13e525901d6bc97 (patch) | |
| tree | e7d3331b6a0cbb17af0e989de685887f8069bfa0 /django/newforms | |
| parent | 1b0d6b942c39bf0f5105be2d47e6e844e43a2659 (diff) | |
boulder-oracle-sprint: Merged to [4077] of trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@4083 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/newforms')
| -rw-r--r-- | django/newforms/fields.py | 24 | ||||
| -rw-r--r-- | django/newforms/forms.py | 38 | ||||
| -rw-r--r-- | django/newforms/util.py | 4 | ||||
| -rw-r--r-- | django/newforms/widgets.py | 108 |
4 files changed, 110 insertions, 64 deletions
diff --git a/django/newforms/fields.py b/django/newforms/fields.py index b9e2ed35c7..179555fc77 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -2,7 +2,7 @@ Field classes """ -from util import ValidationError, DEFAULT_ENCODING +from util import ValidationError, DEFAULT_ENCODING, smart_unicode from widgets import TextInput, CheckboxInput, Select, SelectMultiple import datetime import re @@ -55,10 +55,7 @@ class CharField(Field): "Validates max_length and min_length. Returns a Unicode object." Field.clean(self, value) if value in EMPTY_VALUES: value = u'' - if not isinstance(value, basestring): - value = unicode(str(value), DEFAULT_ENCODING) - elif not isinstance(value, unicode): - value = unicode(value, DEFAULT_ENCODING) + value = smart_unicode(value) if self.max_length is not None and len(value) > self.max_length: raise ValidationError(u'Ensure this value has at most %d characters.' % self.max_length) if self.min_length is not None and len(value) < self.min_length: @@ -165,10 +162,7 @@ class RegexField(Field): """ Field.clean(self, value) if value in EMPTY_VALUES: value = u'' - if not isinstance(value, basestring): - value = unicode(str(value), DEFAULT_ENCODING) - elif not isinstance(value, unicode): - value = unicode(value, DEFAULT_ENCODING) + value = smart_unicode(value) if not self.regex.search(value): raise ValidationError(self.error_message) return value @@ -244,10 +238,7 @@ class ChoiceField(Field): """ value = Field.clean(self, value) if value in EMPTY_VALUES: value = u'' - if not isinstance(value, basestring): - value = unicode(str(value), DEFAULT_ENCODING) - elif not isinstance(value, unicode): - value = unicode(value, DEFAULT_ENCODING) + value = smart_unicode(value) valid_values = set([str(k) for k, v in self.choices]) if value not in valid_values: raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value) @@ -267,11 +258,8 @@ class MultipleChoiceField(ChoiceField): raise ValidationError(u'This field is required.') new_value = [] for val in value: - if not isinstance(val, basestring): - value = unicode(str(val), DEFAULT_ENCODING) - elif not isinstance(val, unicode): - value = unicode(val, DEFAULT_ENCODING) - new_value.append(value) + val = smart_unicode(val) + new_value.append(val) # Validate that each value in the value list is in self.choices. valid_values = set([k for k, v in self.choices]) for val in new_value: diff --git a/django/newforms/forms.py b/django/newforms/forms.py index e490d0d5f9..2730bfe4e4 100644 --- a/django/newforms/forms.py +++ b/django/newforms/forms.py @@ -23,8 +23,9 @@ class Form(object): "A collection of Fields, plus their associated data." __metaclass__ = DeclarativeFieldsMetaclass - def __init__(self, data=None): # TODO: prefix stuff + def __init__(self, data=None, auto_id=False): # TODO: prefix stuff self.data = data or {} + self.auto_id = auto_id self.clean_data = None # Stores the data after clean() has been called. self.__errors = None # Stores the errors after clean() has been called. @@ -63,17 +64,15 @@ class Form(object): return not bool(self.errors()) def as_table(self): - "Returns this form rendered as an HTML <table>." - output = u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()]) - return '<table>\n%s\n</table>' % output + "Returns this form rendered as HTML <tr>s -- excluding the <table></table>." + return u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()]) def as_ul(self): - "Returns this form rendered as an HTML <ul>." - output = u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()]) - return '<ul>\n%s\n</ul>' % output + "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>." + return u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()]) def as_table_with_errors(self): - "Returns this form rendered as an HTML <table>, with errors." + "Returns this form rendered as HTML <tr>s, with errors." output = [] if self.errors().get(NON_FIELD_ERRORS): # Errors not corresponding to a particular field are displayed at the top. @@ -83,10 +82,10 @@ class Form(object): if bf.errors: output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])) output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf)) - return '<table>\n%s\n</table>' % '\n'.join(output) + return u'\n'.join(output) def as_ul_with_errors(self): - "Returns this form rendered as an HTML <ul>, with errors." + "Returns this form rendered as HTML <li>s, with errors." output = [] if self.errors().get(NON_FIELD_ERRORS): # Errors not corresponding to a particular field are displayed at the top. @@ -98,7 +97,7 @@ class Form(object): line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]) line += '%s: %s</li>' % (pretty_name(name), bf) output.append(line) - return '<ul>\n%s\n</ul>' % '\n'.join(output) + return u'\n'.join(output) def full_clean(self): """ @@ -156,6 +155,10 @@ class BoundField(object): errors = property(_errors) def as_widget(self, widget, attrs=None): + attrs = attrs or {} + auto_id = self.auto_id + if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_id: + attrs['id'] = auto_id return widget.render(self._name, self._form.data.get(self._name, None), attrs=attrs) def as_text(self, attrs=None): @@ -167,3 +170,16 @@ class BoundField(object): def as_textarea(self, attrs=None): "Returns a string of HTML for representing this as a <textarea>." return self.as_widget(Textarea(), attrs) + + def _auto_id(self): + """ + Calculates and returns the ID attribute for this BoundField, if the + associated Form has specified auto_id. Returns an empty string otherwise. + """ + auto_id = self._form.auto_id + if auto_id and '%s' in str(auto_id): + return str(auto_id) % self._name + elif auto_id: + return self._name + return '' + auto_id = property(_auto_id) diff --git a/django/newforms/util.py b/django/newforms/util.py index 3887010c85..a5cc4932ea 100644 --- a/django/newforms/util.py +++ b/django/newforms/util.py @@ -2,7 +2,9 @@ DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this. def smart_unicode(s): - if not isinstance(s, unicode): + if not isinstance(s, basestring): + s = unicode(str(s)) + elif not isinstance(s, unicode): s = unicode(s, DEFAULT_ENCODING) return s diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py index 5ec27653cf..318c76e55d 100644 --- a/django/newforms/widgets.py +++ b/django/newforms/widgets.py @@ -5,9 +5,10 @@ HTML Widget classes __all__ = ( 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput', 'Textarea', 'CheckboxInput', - 'Select', 'SelectMultiple', + 'Select', 'SelectMultiple', 'RadioSelect', ) +from util import smart_unicode from django.utils.html import escape from itertools import chain @@ -16,9 +17,9 @@ try: except NameError: from sets import Set as set # Python 2.3 fallback -# Converts a dictionary to a single string with key="value", XML-style. -# Assumes keys do not need to be XML-escaped. -flatatt = lambda attrs: ' '.join(['%s="%s"' % (k, escape(v)) for k, v in attrs.items()]) +# Converts a dictionary to a single string with key="value", XML-style with +# a leading space. Assumes keys do not need to be XML-escaped. +flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()]) class Widget(object): requires_data_list = False # Determines whether render()'s 'value' argument should be a list. @@ -28,16 +29,23 @@ class Widget(object): def render(self, name, value): raise NotImplementedError + def build_attrs(self, extra_attrs=None, **kwargs): + attrs = dict(self.attrs, **kwargs) + if extra_attrs: + attrs.update(extra_attrs) + return attrs + class Input(Widget): - "Base class for all <input> widgets (except type='checkbox', which is special)" + """ + Base class for all <input> widgets (except type='checkbox' and + type='radio', which are special). + """ input_type = None # Subclasses must define this. def render(self, name, value, attrs=None): if value is None: value = '' - final_attrs = dict(self.attrs, type=self.input_type, name=name) - if attrs: - final_attrs.update(attrs) - if value != '': final_attrs['value'] = value # Only add the 'value' attribute if a value is non-empty. - return u'<input %s />' % flatatt(final_attrs) + final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) + if value != '': final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty. + return u'<input%s />' % flatatt(final_attrs) class TextInput(Input): input_type = 'text' @@ -54,18 +62,15 @@ class FileInput(Input): class Textarea(Widget): def render(self, name, value, attrs=None): if value is None: value = '' - final_attrs = dict(self.attrs, name=name) - if attrs: - final_attrs.update(attrs) - return u'<textarea %s>%s</textarea>' % (flatatt(final_attrs), escape(value)) + value = smart_unicode(value) + final_attrs = self.build_attrs(attrs, name=name) + return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value)) class CheckboxInput(Widget): def render(self, name, value, attrs=None): - final_attrs = dict(self.attrs, type='checkbox', name=name) - if attrs: - final_attrs.update(attrs) + final_attrs = self.build_attrs(attrs, type='checkbox', name=name) if value: final_attrs['checked'] = 'checked' - return u'<input %s />' % flatatt(final_attrs) + return u'<input%s />' % flatatt(final_attrs) class Select(Widget): def __init__(self, attrs=None, choices=()): @@ -75,14 +80,13 @@ class Select(Widget): def render(self, name, value, attrs=None, choices=()): if value is None: value = '' - final_attrs = dict(self.attrs, name=name) - if attrs: - final_attrs.update(attrs) - output = [u'<select %s>' % flatatt(final_attrs)] - str_value = str(value) # Normalize to string. + final_attrs = self.build_attrs(attrs, name=name) + output = [u'<select%s>' % flatatt(final_attrs)] + str_value = smart_unicode(value) # Normalize to string. for option_value, option_label in chain(self.choices, choices): - selected_html = (str(option_value) == str_value) and ' selected="selected"' or '' - output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label))) + option_value = smart_unicode(option_value) + selected_html = (option_value == str_value) and u' selected="selected"' or '' + output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(smart_unicode(option_label)))) output.append(u'</select>') return u'\n'.join(output) @@ -95,19 +99,55 @@ class SelectMultiple(Widget): def render(self, name, value, attrs=None, choices=()): if value is None: value = [] - final_attrs = dict(self.attrs, name=name) - if attrs: - final_attrs.update(attrs) - output = [u'<select multiple="multiple" %s>' % flatatt(final_attrs)] - str_values = set([str(v) for v in value]) # Normalize to strings. + final_attrs = self.build_attrs(attrs, name=name) + output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)] + str_values = set([smart_unicode(v) for v in value]) # Normalize to strings. for option_value, option_label in chain(self.choices, choices): - selected_html = (str(option_value) in str_values) and ' selected="selected"' or '' - output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label))) + option_value = smart_unicode(option_value) + selected_html = (option_value in str_values) and ' selected="selected"' or '' + output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(smart_unicode(option_label)))) output.append(u'</select>') return u'\n'.join(output) -class RadioSelect(Widget): - pass +class RadioInput(object): + "An object used by RadioFieldRenderer that represents a single <input type='radio'>." + def __init__(self, name, value, attrs, choice): + self.name, self.value = name, value + self.attrs = attrs or {} + self.choice_value, self.choice_label = choice + + def __str__(self): + return u'<label>%s %s</label>' % (self.tag(), self.choice_label) + + def is_checked(self): + return self.value == smart_unicode(self.choice_value) + + def tag(self): + final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) + if self.is_checked(): + final_attrs['checked'] = 'checked' + return u'<input%s />' % flatatt(final_attrs) + +class RadioFieldRenderer(object): + "An object used by RadioSelect to enable customization of radio widgets." + def __init__(self, name, value, attrs, choices): + self.name, self.value, self.attrs = name, value, attrs + self.choices = choices + + def __iter__(self): + for choice in self.choices: + yield RadioInput(self.name, self.value, self.attrs, choice) + + def __str__(self): + "Outputs a <ul> for this set of radio fields." + return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self]) + +class RadioSelect(Select): + def render(self, name, value, attrs=None, choices=()): + "Returns a RadioFieldRenderer instance rather than a Unicode string." + if value is None: value = '' + str_value = smart_unicode(value) # Normalize to string. + return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices))) class CheckboxSelectMultiple(Widget): pass |
