Ticket #6405: combofield_multivaluefield_pass_initial_trough.2.diff

File combofield_multivaluefield_pass_initial_trough.2.diff, 9.3 KB (added by piquadrat, 6 years ago)

Synced patch to trunk (other patch was in the wrong direction, sorry)

  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index ccb54d8..5da3a01 100644
    a b except ImportError: 
    439439    # It's OK if Django settings aren't configured.
    440440    URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
    441441
    442 
    443 class FileField(Field):
     442class FieldWithInitial(object):
     443    pass
     444 
     445class FileField(Field, FieldWithInitial):
    444446    widget = FileInput
    445447    default_error_messages = {
    446448        'invalid': _(u"No file was submitted. Check the encoding type on the form."),
    class FileField(Field): 
    454456    def clean(self, data, initial=None):
    455457        super(FileField, self).clean(initial or data)
    456458        if not self.required and data in EMPTY_VALUES:
    457             return None
     459            return initial or None
    458460        elif not data and initial:
    459461            return initial
    460462
    class MultipleChoiceField(ChoiceField): 
    714716                raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
    715717        return new_value
    716718
    717 class ComboField(Field):
     719class ComboField(Field, FieldWithInitial):
    718720    """
    719721    A Field whose clean() method calls multiple Field clean() methods.
    720722    """
    class ComboField(Field): 
    727729            f.required = False
    728730        self.fields = fields
    729731
    730     def clean(self, value):
     732    def clean(self, value, initial=None):
    731733        """
    732734        Validates the given value against all of self.fields, which is a
    733735        list of Field instances.
    734736        """
    735         super(ComboField, self).clean(value)
     737        super(ComboField, self).clean(initial or value)
    736738        for field in self.fields:
    737             value = field.clean(value)
     739            if isinstance(field, FieldWithInitial):
     740                value = field.clean(value, initial)
     741            else:
     742                value = field.clean(value)
    738743        return value
    739744
    740 class MultiValueField(Field):
     745class MultiValueField(Field, FieldWithInitial):
    741746    """
    742747    A Field that aggregates the logic of multiple Fields.
    743748
    class MultiValueField(Field): 
    767772            f.required = False
    768773        self.fields = fields
    769774
    770     def clean(self, value):
     775    def clean(self, value, initial=None):
    771776        """
    772777        Validates every value in the given list. A value is validated against
    773778        the corresponding Field in self.fields.
    class MultiValueField(Field): 
    776781        fields=(DateField(), TimeField()), clean() would call
    777782        DateField.clean(value[0]) and TimeField.clean(value[1]).
    778783        """
    779         clean_data = []
     784        clean_data, value_list, initial_list, takes_initial_list = [], [], [], []
     785        value, initial = value or [], initial or []
    780786        errors = ErrorList()
    781787        if not value or isinstance(value, (list, tuple)):
    782             if not value or not [v for v in value if v not in EMPTY_VALUES]:
     788            for i, field in enumerate(self.fields):
     789                try:
     790                    field_value = value[i]
     791                except IndexError:
     792                    field_value = None
     793                value_list.append(field_value)
     794                try:
     795                    field_initial = initial[i]
     796                except IndexError:
     797                    field_initial = None
     798                initial_list.append(field_initial)
     799                takes_initial_list.append(isinstance(field, FieldWithInitial))
     800
     801            if not value or not [(v, i, t) for v, i, t in \
     802                zip(value_list, initial_list, takes_initial_list) \
     803                if v not in EMPTY_VALUES or t and i not in EMPTY_VALUES]:
     804
    783805                if self.required:
    784806                    raise ValidationError(self.error_messages['required'])
    785807                else:
    class MultiValueField(Field): 
    787809        else:
    788810            raise ValidationError(self.error_messages['invalid'])
    789811        for i, field in enumerate(self.fields):
     812            field_value = value_list[i]
     813            field_initial = initial_list[i]
     814            takes_initial = takes_initial_list[i]
     815            if self.required and not ( (takes_initial and field_initial not in EMPTY_VALUES) or
     816                (field_value not in EMPTY_VALUES) ):
     817                 raise ValidationError(self.error_messages['required'])
    790818            try:
    791                 field_value = value[i]
    792             except IndexError:
    793                 field_value = None
    794             if self.required and field_value in EMPTY_VALUES:
    795                 raise ValidationError(self.error_messages['required'])
    796             try:
    797                 clean_data.append(field.clean(field_value))
     819                if takes_initial:
     820                    clean_data.append(field.clean(field_value, field_initial))
     821                else:
     822                    clean_data.append(field.clean(field_value))
    798823            except ValidationError, e:
    799824                # Collect all validation errors in a single list, which we'll
    800825                # raise at the end of clean(), rather than raising a single
    class MultiValueField(Field): 
    802827                errors.extend(e.messages)
    803828        if errors:
    804829            raise ValidationError(errors)
    805         return self.compress(clean_data)
     830        return self.compress(clean_data, initial_list)
    806831
    807     def compress(self, data_list):
     832    def compress(self, data_list, initial_list=[]):
    808833        """
    809834        Returns a single value for the given list of values. The values can be
    810835        assumed to be valid.
    class SplitDateTimeField(MultiValueField): 
    860885        )
    861886        super(SplitDateTimeField, self).__init__(fields, *args, **kwargs)
    862887
    863     def compress(self, data_list):
     888    def compress(self, data_list, initial_list=[]):
    864889        if data_list:
    865890            # Raise a validation error if time or date is empty
    866891            # (possible if SplitDateTimeField has required=False).
  • django/forms/forms.py

    diff --git a/django/forms/forms.py b/django/forms/forms.py
    index f5bea10..45c1de7 100644
    a b from django.utils.html import conditional_escape 
    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 Media, media_property, TextInput, Textarea
    1414from util import flatatt, ErrorDict, ErrorList, ValidationError
    1515
    class BaseForm(StrAndUnicode): 
    224224            # widgets split data over several HTML fields.
    225225            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
    226226            try:
    227                 if isinstance(field, FileField):
     227                if isinstance(field, FieldWithInitial):
    228228                    initial = self.initial.get(name, field.initial)
    229229                    value = field.clean(value, initial)
    230230                else:
  • tests/regressiontests/forms/extra.py

    diff --git a/tests/regressiontests/forms/extra.py b/tests/regressiontests/forms/extra.py
    index 80f7ef6..5ba45b2 100644
    a b True 
    276276...         )
    277277...         super(ComplexField, self).__init__(fields, required, widget, label, initial)
    278278...
    279 ...     def compress(self, data_list):
     279...     def compress(self, data_list, initial_list=[]):
    280280...         if data_list:
    281281...             return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2])
    282282...         return None
  • tests/regressiontests/forms/fields.py

    diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py
    index fa5a62a..eabded7 100644
    a b ValidationError: [u'Enter a valid e-mail address.'] 
    12901290u''
    12911291>>> f.clean(None)
    12921292u''
     1293>>> f = ComboField(fields=[FileField(), CharField(max_length=20)])
     1294>>> f.clean('data', 'some_file.txt')
     1295Traceback (most recent call last):
     1296...   
     1297ValidationError: [u'No file was submitted. Check the encoding type on the form.']
     1298>>> f.clean('', 'some_file.txt')
     1299u'some_file.txt'
     1300>>> f.clean('', None)
     1301Traceback (most recent call last):
     1302...
     1303ValidationError: [u'This field is required.']
     1304>>> f.clean({'filename': 'filename.txt', 'content':'something'})
     1305u'filename.txt'
     1306>>> f.clean({'filename': 'too_long_filename_for_this_combofield_here.txt', 'content':'something'})
     1307Traceback (most recent call last):
     1308...
     1309ValidationError: [u'Ensure this value has at most 20 characters (it has 46).']
     1310 
     1311# MultiValueField ##########################################################
     1312
     1313>>> f = MultiValueField(fields=[FileField(), CharField(max_length=20)])
     1314>>> f.clean([{'filename': 'some_file.txt', 'content':'something'}, 'Some text'])
     1315Traceback (most recent call last):
     1316...
     1317NotImplementedError: Subclasses must implement this method.
     1318>>> f.clean([{'filename': 'some_file.txt', 'content':'something'}, 'alotoftext_more_than_20_chars'])
     1319Traceback (most recent call last):
     1320...
     1321ValidationError: [u'Ensure this value has at most 20 characters (it has 29).']
     1322>>> f.clean([None, None])
     1323Traceback (most recent call last):
     1324...
     1325ValidationError: [u'This field is required.']
     1326>>> f.clean([None, 'Some text'], ['some_file.txt', None])
     1327Traceback (most recent call last):
     1328...
     1329NotImplementedError: Subclasses must implement this method.
    12931330
    12941331# FilePathField ###############################################################
    12951332
Back to Top