diff options
author | Arthur Koziel <arthur@arthurkoziel.com> | 2010-09-13 00:04:27 +0000 |
---|---|---|
committer | Arthur Koziel <arthur@arthurkoziel.com> | 2010-09-13 00:04:27 +0000 |
commit | dd49269c7db008b2567f50cb03c4d3d9b321daa1 (patch) | |
tree | 326dd25bb045ac016cda7966b43cbdfe1f67d699 /django/forms | |
parent | c9b188c4ec939abbe48dae5a371276742e64b6b8 (diff) | |
download | django-soc2010/app-loading.tar.gz |
[soc2010/app-loading] merged trunkarchive/soc2010/app-loadingsoc2010/app-loading
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/app-loading@13818 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/forms')
-rw-r--r-- | django/forms/fields.py | 5 | ||||
-rw-r--r-- | django/forms/forms.py | 20 | ||||
-rw-r--r-- | django/forms/formsets.py | 14 | ||||
-rw-r--r-- | django/forms/models.py | 58 | ||||
-rw-r--r-- | django/forms/widgets.py | 45 |
5 files changed, 91 insertions, 51 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py index 0bae4ba157..de14a5c8a8 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -127,6 +127,9 @@ class Field(object): self.validators = self.default_validators + validators + def prepare_value(self, value): + return value + def to_python(self, value): return value @@ -396,6 +399,8 @@ class DateTimeField(Field): # components: date and time. if len(value) != 2: raise ValidationError(self.error_messages['invalid']) + if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES: + return None value = '%s %s' % tuple(value) for format in self.input_formats or formats.get_format('DATETIME_INPUT_FORMATS'): try: diff --git a/django/forms/forms.py b/django/forms/forms.py index b3718efa9a..7b2bc66fc7 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -18,10 +18,10 @@ __all__ = ('BaseForm', 'Form') NON_FIELD_ERRORS = '__all__' def pretty_name(name): - """Converts 'first_name' to 'First name'""" - if not name: - return u'' - return name.replace('_', ' ').capitalize() + """Converts 'first_name' to 'First name'""" + if not name: + return u'' + return name.replace('_', ' ').capitalize() def get_declared_fields(bases, attrs, with_base_fields=True): """ @@ -35,7 +35,7 @@ def get_declared_fields(bases, attrs, with_base_fields=True): Also integrates any additional media definitions """ fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] - fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) + fields.sort(key=lambda x: x[1].creation_counter) # If this class is subclassing another Form, add that Form's fields. # Note that we loop over the bases in *reverse*. This is necessary in @@ -213,7 +213,7 @@ class BaseForm(StrAndUnicode): normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', error_row = u'<tr><td colspan="2">%s</td></tr>', row_ender = u'</td></tr>', - help_text_html = u'<br />%s', + help_text_html = u'<br /><span class="helptext">%s</span>', errors_on_separate_row = False) def as_ul(self): @@ -222,7 +222,7 @@ class BaseForm(StrAndUnicode): normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', error_row = u'<li>%s</li>', row_ender = '</li>', - help_text_html = u' %s', + help_text_html = u' <span class="helptext">%s</span>', errors_on_separate_row = False) def as_p(self): @@ -231,7 +231,7 @@ class BaseForm(StrAndUnicode): normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', error_row = u'%s', row_ender = '</p>', - help_text_html = u' %s', + help_text_html = u' <span class="helptext">%s</span>', errors_on_separate_row = True) def non_field_errors(self): @@ -423,6 +423,7 @@ class BoundField(StrAndUnicode): """ if not widget: widget = self.field.widget + attrs = attrs or {} auto_id = self.auto_id if auto_id and 'id' not in attrs and 'id' not in widget.attrs: @@ -430,6 +431,7 @@ class BoundField(StrAndUnicode): attrs['id'] = auto_id else: attrs['id'] = self.html_initial_id + if not self.form.is_bound: data = self.form.initial.get(self.name, self.field.initial) if callable(data): @@ -439,6 +441,8 @@ class BoundField(StrAndUnicode): data = self.form.initial.get(self.name, self.field.initial) else: data = self.data + data = self.field.prepare_value(data) + if not only_initial: name = self.html_name else: diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 3804f2b3c1..508950eee9 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -199,14 +199,12 @@ class BaseFormSet(StrAndUnicode): # A sort function to order things numerically ascending, but # None should be sorted below anything else. Allowing None as # a comparison value makes it so we can leave ordering fields - # blamk. - def compare_ordering_values(x, y): - if x[1] is None: - return 1 - if y[1] is None: - return -1 - return x[1] - y[1] - self._ordering.sort(compare_ordering_values) + # blank. + def compare_ordering_key(k): + if k[1] is None: + return (1, 0) # +infinity, larger than any number + return (0, k[1]) + self._ordering.sort(key=compare_ordering_key) # Return a list of form.cleaned_data dicts in the order spcified by # the form data. return [self.forms[i[0]] for i in self._ordering] diff --git a/django/forms/models.py b/django/forms/models.py index 8accd61f30..cf465ad30c 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -9,7 +9,8 @@ from django.utils.datastructures import SortedDict from django.utils.text import get_text_list, capfirst from django.utils.translation import ugettext_lazy as _, ugettext -from django.core.exceptions import ValidationError, NON_FIELD_ERRORS +from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, \ + FieldError from django.core.validators import EMPTY_VALUES from util import ErrorList from forms import BaseForm, get_declared_fields @@ -150,7 +151,7 @@ def model_to_dict(instance, fields=None, exclude=None): data[f.name] = f.value_from_object(instance) return data -def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): +def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None): """ Returns a ``SortedDict`` containing form fields for the given model. @@ -175,7 +176,14 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c kwargs = {'widget': widgets[f.name]} else: kwargs = {} - formfield = formfield_callback(f, **kwargs) + + if formfield_callback is None: + formfield = f.formfield(**kwargs) + elif not callable(formfield_callback): + raise TypeError('formfield_callback must be a function or callable') + else: + formfield = formfield_callback(f, **kwargs) + if formfield: field_list.append((f.name, formfield)) else: @@ -198,8 +206,7 @@ class ModelFormOptions(object): class ModelFormMetaclass(type): def __new__(cls, name, bases, attrs): - formfield_callback = attrs.pop('formfield_callback', - lambda f, **kwargs: f.formfield(**kwargs)) + formfield_callback = attrs.pop('formfield_callback', None) try: parents = [b for b in bases if issubclass(b, ModelForm)] except NameError: @@ -218,6 +225,15 @@ class ModelFormMetaclass(type): # If a model is defined, extract form fields from it. fields = fields_for_model(opts.model, opts.fields, opts.exclude, opts.widgets, formfield_callback) + # make sure opts.fields doesn't specify an invalid field + none_model_fields = [k for k, v in fields.iteritems() if not v] + missing_fields = set(none_model_fields) - \ + set(declared_fields.keys()) + if missing_fields: + message = 'Unknown field(s) (%s) specified for %s' + message = message % (', '.join(missing_fields), + opts.model.__name__) + raise FieldError(message) # Override default model fields with any custom declared ones # (plus, include all the other declared fields). fields.update(declared_fields) @@ -376,7 +392,7 @@ class ModelForm(BaseModelForm): __metaclass__ = ModelFormMetaclass def modelform_factory(model, form=ModelForm, fields=None, exclude=None, - formfield_callback=lambda f: f.formfield()): + formfield_callback=None): # Create the inner Meta class. FIXME: ideally, we should be able to # construct a ModelForm without creating and passing in a temporary # inner class. @@ -658,7 +674,7 @@ class BaseModelFormSet(BaseFormSet): form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput) super(BaseModelFormSet, self).add_fields(form, index) -def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), +def modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None): @@ -813,7 +829,7 @@ def inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, - formfield_callback=lambda f: f.formfield()): + formfield_callback=None): """ Returns an ``InlineFormSet`` for the given kwargs. @@ -906,12 +922,7 @@ class ModelChoiceIterator(object): return len(self.queryset) def choice(self, obj): - if self.field.to_field_name: - key = obj.serializable_value(self.field.to_field_name) - else: - key = obj.pk - return (key, self.field.label_from_instance(obj)) - + return (self.field.prepare_value(obj), self.field.label_from_instance(obj)) class ModelChoiceField(ChoiceField): """A ChoiceField whose choices are a model QuerySet.""" @@ -971,8 +982,8 @@ class ModelChoiceField(ChoiceField): return self._choices # Otherwise, execute the QuerySet in self.queryset to determine the - # choices dynamically. Return a fresh QuerySetIterator that has not been - # consumed. Note that we're instantiating a new QuerySetIterator *each* + # choices dynamically. Return a fresh ModelChoiceIterator that has not been + # consumed. Note that we're instantiating a new ModelChoiceIterator *each* # time _get_choices() is called (and, thus, each time self.choices is # accessed) so that we can ensure the QuerySet has not been consumed. This # construct might look complicated but it allows for lazy evaluation of @@ -981,13 +992,21 @@ class ModelChoiceField(ChoiceField): choices = property(_get_choices, ChoiceField._set_choices) + def prepare_value(self, value): + if hasattr(value, '_meta'): + if self.to_field_name: + return value.serializable_value(self.to_field_name) + else: + return value.pk + return super(ModelChoiceField, self).prepare_value(value) + def to_python(self, value): if value in EMPTY_VALUES: return None try: key = self.to_field_name or 'pk' value = self.queryset.get(**{key: value}) - except self.queryset.model.DoesNotExist: + except (ValueError, self.queryset.model.DoesNotExist): raise ValidationError(self.error_messages['invalid_choice']) return value @@ -1030,3 +1049,8 @@ class ModelMultipleChoiceField(ModelChoiceField): if force_unicode(val) not in pks: raise ValidationError(self.error_messages['invalid_choice'] % val) return qs + + def prepare_value(self, value): + if hasattr(value, '__iter__'): + return [super(ModelMultipleChoiceField, self).prepare_value(v) for v in value] + return super(ModelMultipleChoiceField, self).prepare_value(value) diff --git a/django/forms/widgets.py b/django/forms/widgets.py index e3799c69ad..2e16c35d8b 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -229,7 +229,7 @@ class TextInput(Input): class PasswordInput(Input): input_type = 'password' - def __init__(self, attrs=None, render_value=True): + def __init__(self, attrs=None, render_value=False): super(PasswordInput, self).__init__(attrs) self.render_value = render_value @@ -308,9 +308,13 @@ class DateInput(Input): super(DateInput, self).__init__(attrs) if format: self.format = format + self.manual_format = True + else: + self.format = formats.get_format('DATE_INPUT_FORMATS')[0] + self.manual_format = False def _format_value(self, value): - if self.is_localized: + if self.is_localized and not self.manual_format: return formats.localize_input(value) elif hasattr(value, 'strftime'): value = datetime_safe.new_date(value) @@ -336,9 +340,13 @@ class DateTimeInput(Input): super(DateTimeInput, self).__init__(attrs) if format: self.format = format + self.manual_format = True + else: + self.format = formats.get_format('DATETIME_INPUT_FORMATS')[0] + self.manual_format = False def _format_value(self, value): - if self.is_localized: + if self.is_localized and not self.manual_format: return formats.localize_input(value) elif hasattr(value, 'strftime'): value = datetime_safe.new_datetime(value) @@ -364,9 +372,13 @@ class TimeInput(Input): super(TimeInput, self).__init__(attrs) if format: self.format = format + self.manual_format = True + else: + self.format = formats.get_format('TIME_INPUT_FORMATS')[0] + self.manual_format = False def _format_value(self, value): - if self.is_localized: + if self.is_localized and not self.manual_format: return formats.localize_input(value) elif hasattr(value, 'strftime'): return value.strftime(self.format) @@ -438,13 +450,14 @@ class Select(Widget): output.append(u'</select>') return mark_safe(u'\n'.join(output)) + def render_option(self, selected_choices, option_value, option_label): + option_value = force_unicode(option_value) + selected_html = (option_value in selected_choices) and u' selected="selected"' or '' + return u'<option value="%s"%s>%s</option>' % ( + escape(option_value), selected_html, + conditional_escape(force_unicode(option_label))) + def render_options(self, choices, selected_choices): - def render_option(option_value, option_label): - option_value = force_unicode(option_value) - selected_html = (option_value in selected_choices) and u' selected="selected"' or '' - return u'<option value="%s"%s>%s</option>' % ( - escape(option_value), selected_html, - conditional_escape(force_unicode(option_label))) # Normalize to strings. selected_choices = set([force_unicode(v) for v in selected_choices]) output = [] @@ -452,10 +465,10 @@ class Select(Widget): if isinstance(option_label, (list, tuple)): output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value))) for option in option_label: - output.append(render_option(*option)) + output.append(self.render_option(selected_choices, *option)) output.append(u'</optgroup>') else: - output.append(render_option(option_value, option_label)) + output.append(self.render_option(selected_choices, option_value, option_label)) return u'\n'.join(output) class NullBooleanSelect(Select): @@ -751,12 +764,8 @@ class SplitDateTimeWidget(MultiWidget): time_format = TimeInput.format def __init__(self, attrs=None, date_format=None, time_format=None): - if date_format: - self.date_format = date_format - if time_format: - self.time_format = time_format - widgets = (DateInput(attrs=attrs, format=self.date_format), - TimeInput(attrs=attrs, format=self.time_format)) + widgets = (DateInput(attrs=attrs, format=date_format), + TimeInput(attrs=attrs, format=time_format)) super(SplitDateTimeWidget, self).__init__(widgets, attrs) def decompress(self, value): |