Ticket #6405: combofield_multivaluefield_pass_initial_trough.diff

File combofield_multivaluefield_pass_initial_trough.diff, 9.2 KB (added by Øyvind Saltvik <oyvind@…>, 7 years ago)

a new approach using isinstance and a mixin

  • django/newforms/fields.py

     
    413413    # It's OK if Django settings aren't configured.
    414414    URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
    415415
     416class FieldWithInitial(object):
     417    pass
     418
    416419class UploadedFile(StrAndUnicode):
    417420    "A wrapper for files uploaded in a FileField"
    418421    def __init__(self, filename, content):
     
    426429        """
    427430        return self.filename
    428431
    429 class FileField(Field):
     432class FileField(Field, FieldWithInitial):
    430433    widget = FileInput
    431434    default_error_messages = {
    432435        'invalid': _(u"No file was submitted. Check the encoding type on the form."),
     
    440443    def clean(self, data, initial=None):
    441444        super(FileField, self).clean(initial or data)
    442445        if not self.required and data in EMPTY_VALUES:
    443             return None
     446            return initial or None
    444447        elif not data and initial:
    445448            return initial
    446449        try:
     
    615618                raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
    616619        return new_value
    617620
    618 class ComboField(Field):
     621class ComboField(Field, FieldWithInitial):
    619622    """
    620623    A Field whose clean() method calls multiple Field clean() methods.
    621624    """
     
    628631            f.required = False
    629632        self.fields = fields
    630633
    631     def clean(self, value):
     634    def clean(self, value, initial=None):
    632635        """
    633636        Validates the given value against all of self.fields, which is a
    634637        list of Field instances.
    635638        """
    636         super(ComboField, self).clean(value)
     639        super(ComboField, self).clean(initial or value)
    637640        for field in self.fields:
    638             value = field.clean(value)
     641            if isinstance(field, FieldWithInitial):
     642                value = field.clean(value, initial)
     643            else:
     644                value = field.clean(value)
    639645        return value
    640646
    641 class MultiValueField(Field):
     647class MultiValueField(Field, FieldWithInitial):
    642648    """
    643649    A Field that aggregates the logic of multiple Fields.
    644650
     
    668674            f.required = False
    669675        self.fields = fields
    670676
    671     def clean(self, value):
     677    def clean(self, value, initial=None):
    672678        """
    673679        Validates every value in the given list. A value is validated against
    674680        the corresponding Field in self.fields.
     
    677683        fields=(DateField(), TimeField()), clean() would call
    678684        DateField.clean(value[0]) and TimeField.clean(value[1]).
    679685        """
    680         clean_data = []
     686        clean_data, value_list, initial_list, takes_initial_list = [], [], [], []
     687        value, initial = value or [], initial or []
    681688        errors = ErrorList()
    682689        if not value or isinstance(value, (list, tuple)):
    683             if not value or not [v for v in value if v not in EMPTY_VALUES]:
     690            for i, field in enumerate(self.fields):
     691                try:
     692                    field_value = value[i]
     693                except IndexError:
     694                    field_value = None
     695                value_list.append(field_value)
     696                try:
     697                    field_initial = initial[i]
     698                except IndexError:
     699                    field_initial = None
     700                initial_list.append(field_initial)
     701                takes_initial_list.append(isinstance(field, FieldWithInitial))
     702
     703            if not value or not [(v, i, t) for v, i, t in \
     704                zip(value_list, initial_list, takes_initial_list) \
     705                if v not in EMPTY_VALUES or t and i not in EMPTY_VALUES]:
     706
    684707                if self.required:
    685708                    raise ValidationError(self.error_messages['required'])
    686709                else:
     
    688711        else:
    689712            raise ValidationError(self.error_messages['invalid'])
    690713        for i, field in enumerate(self.fields):
     714            field_value = value_list[i]
     715            field_initial = initial_list[i]
     716            takes_initial = takes_initial_list[i]
     717            if self.required and not ( (takes_initial and field_initial not in EMPTY_VALUES) or
     718                (field_value not in EMPTY_VALUES) ):
     719                 raise ValidationError(self.error_messages['required'])
    691720            try:
    692                 field_value = value[i]
    693             except IndexError:
    694                 field_value = None
    695             if self.required and field_value in EMPTY_VALUES:
    696                 raise ValidationError(self.error_messages['required'])
    697             try:
    698                 clean_data.append(field.clean(field_value))
     721                if takes_initial:
     722                    clean_data.append(field.clean(field_value, field_initial))
     723                else:
     724                    clean_data.append(field.clean(field_value))
    699725            except ValidationError, e:
    700726                # Collect all validation errors in a single list, which we'll
    701727                # raise at the end of clean(), rather than raising a single
     
    703729                errors.extend(e.messages)
    704730        if errors:
    705731            raise ValidationError(errors)
    706         return self.compress(clean_data)
     732        return self.compress(clean_data, initial_list)
    707733
    708     def compress(self, data_list):
     734    def compress(self, data_list, initial_list=[]):
    709735        """
    710736        Returns a single value for the given list of values. The values can be
    711737        assumed to be valid.
     
    732758        )
    733759        super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
    734760
    735     def compress(self, data_list):
     761    def compress(self, data_list, initial_list=[]):
    736762        if data_list:
    737763            # Raise a validation error if time or date is empty
    738764            # (possible if SplitDateTimeField has required=False).
  • django/newforms/forms.py

     
    99from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
    1010from django.utils.safestring import mark_safe
    1111
    12 from fields import Field, FileField
     12from fields import Field, FieldWithInitial
    1313from widgets import TextInput, Textarea
    1414from util import flatatt, ErrorDict, ErrorList, ValidationError
    1515
     
    182182            # widgets split data over several HTML fields.
    183183            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
    184184            try:
    185                 if isinstance(field, FileField):
     185                if isinstance(field, FieldWithInitial):
    186186                    initial = self.initial.get(name, field.initial)
    187187                    value = field.clean(value, initial)
    188188                else:
  • tests/regressiontests/forms/extra.py

     
    209209...         )
    210210...         super(ComplexField, self).__init__(fields, required, widget, label, initial)
    211211...
    212 ...     def compress(self, data_list):
     212...     def compress(self, data_list, initial_list=[]):
    213213...         if data_list:
    214214...             return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2])
    215215...         return None
  • tests/regressiontests/forms/fields.py

     
    11321132u''
    11331133>>> f.clean(None)
    11341134u''
     1135>>> f = ComboField(fields=[FileField(), CharField(max_length=20)])
     1136>>> f.clean('data', 'some_file.txt')
     1137Traceback (most recent call last):
     1138...   
     1139ValidationError: [u'No file was submitted. Check the encoding type on the form.']
     1140>>> f.clean('', 'some_file.txt')
     1141u'some_file.txt'
     1142>>> f.clean('', None)
     1143Traceback (most recent call last):
     1144...
     1145ValidationError: [u'This field is required.']
     1146>>> f.clean({'filename': 'filename.txt', 'content':'something'})
     1147u'filename.txt'
     1148>>> f.clean({'filename': 'too_long_filename_for_this_combofield_here.txt', 'content':'something'})
     1149Traceback (most recent call last):
     1150...
     1151ValidationError: [u'Ensure this value has at most 20 characters (it has 46).']
    11351152
     1153# MultiValueField ##########################################################
     1154
     1155>>> f = MultiValueField(fields=[FileField(), CharField(max_length=20)])
     1156>>> f.clean([{'filename': 'some_file.txt', 'content':'something'}, 'Some text'])
     1157Traceback (most recent call last):
     1158...
     1159NotImplementedError: Subclasses must implement this method.
     1160>>> f.clean([{'filename': 'some_file.txt', 'content':'something'}, 'alotoftext_more_than_20_chars'])
     1161Traceback (most recent call last):
     1162...
     1163ValidationError: [u'Ensure this value has at most 20 characters (it has 29).']
     1164>>> f.clean([None, None])
     1165Traceback (most recent call last):
     1166...
     1167ValidationError: [u'This field is required.']
     1168>>> f.clean([None, 'Some text'], ['some_file.txt', None])
     1169Traceback (most recent call last):
     1170...
     1171NotImplementedError: Subclasses must implement this method.
     1172
    11361173# SplitDateTimeField ##########################################################
    11371174
    11381175>>> f = SplitDateTimeField()
Back to Top