Django

Code

Ticket #6302: pass_initial_through_with_tests_mulitvaluefield.diff

File pass_initial_through_with_tests_mulitvaluefield.diff, 15.2 kB (added by Øyvind Saltvik <oyvind@saltvik.no>, 9 months ago)

MultiValueField? works too, is this overkill?

  • django/db/models/fields/__init__.py

    old new  
    797797        return os.path.normpath(f) 
    798798 
    799799    def save_form_data(self, instance, data): 
    800         if data: 
     800        from django.newforms.fields import UploadedFile 
     801        if data and isinstance(data, UploadedFile): 
    801802            getattr(instance, "save_%s_file" % self.name)(data.filename, data.content, save=False) 
    802803 
    803804    def formfield(self, **kwargs): 
    804805        defaults = {'form_class': forms.FileField} 
    805         # If a file has been provided previously, then the form doesn't require 
    806         # that a new file is provided this time. 
    807         if 'initial' in kwargs: 
    808             defaults['required'] = False 
    809806        defaults.update(kwargs) 
    810807        return super(FileField, self).formfield(**defaults) 
    811808 
  • django/newforms/fields.py

    old new  
    428428 
    429429class FileField(Field): 
    430430    widget = FileInput 
     431    takes_initial = True 
    431432    default_error_messages = { 
    432433        'invalid': _(u"No file was submitted. Check the encoding type on the form."), 
    433434        'missing': _(u"No file was submitted."), 
     
    437438    def __init__(self, *args, **kwargs): 
    438439        super(FileField, self).__init__(*args, **kwargs) 
    439440 
    440     def clean(self, data): 
    441         super(FileField, self).clean(data) 
     441    def clean(self, data, initial=None): 
     442        super(FileField, self).clean(initial or data) 
    442443        if not self.required and data in EMPTY_VALUES: 
    443             return None 
     444            return initial or None 
     445        elif not data and initial: 
     446            return initial 
    444447        try: 
    445448            f = UploadedFile(data['filename'], data['content']) 
    446449        except TypeError: 
     
    456459        'invalid_image': _(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."), 
    457460    } 
    458461 
    459     def clean(self, data): 
     462    def clean(self, data, initial=None): 
    460463        """ 
    461464        Checks that the file-upload field data contains a valid image (GIF, JPG, 
    462465        PNG, possibly others -- whatever the Python Imaging Library supports). 
    463466        """ 
    464         f = super(ImageField, self).clean(data
     467        f = super(ImageField, self).clean(data, initial
    465468        if f is None: 
    466469            return None 
    467470        from PIL import Image 
     
    614617        return new_value 
    615618 
    616619class ComboField(Field): 
     620    takes_initial = True 
     621 
    617622    """ 
    618623    A Field whose clean() method calls multiple Field clean() methods. 
    619624    """ 
     
    626631            f.required = False 
    627632        self.fields = fields 
    628633 
    629     def clean(self, value): 
     634    def clean(self, value, initial=None): 
    630635        """ 
    631636        Validates the given value against all of self.fields, which is a 
    632637        list of Field instances. 
    633638        """ 
    634         super(ComboField, self).clean(value) 
     639        super(ComboField, self).clean(initial or value) 
    635640        for field in self.fields: 
    636             value = field.clean(value) 
     641            if hasattr(field, 'takes_initial'): 
     642                value = field.clean(value, initial) 
     643            else: 
     644                value = field.clean(value) 
    637645        return value 
    638646 
    639647class MultiValueField(Field): 
     
    653661 
    654662    You'll probably want to use this with MultiWidget. 
    655663    """ 
     664    takes_initial = True 
    656665    default_error_messages = { 
    657666        'invalid': _(u'Enter a list of values.'), 
    658667    } 
     
    666675            f.required = False 
    667676        self.fields = fields 
    668677 
    669     def clean(self, value): 
     678    def clean(self, value, initial=None): 
    670679        """ 
    671680        Validates every value in the given list. A value is validated against 
    672681        the corresponding Field in self.fields. 
     
    675684        fields=(DateField(), TimeField()), clean() would call 
    676685        DateField.clean(value[0]) and TimeField.clean(value[1]). 
    677686        """ 
    678         clean_data = [] 
     687        clean_data, value_list, initial_list, takes_initial_list = [], [], [], [] 
     688        value, initial = value or [], initial or [] 
    679689        errors = ErrorList() 
    680690        if not value or isinstance(value, (list, tuple)): 
    681             if not value or not [v for v in value if v not in EMPTY_VALUES]: 
     691            for i, field in enumerate(self.fields): 
     692                try: 
     693                    field_value = value[i] 
     694                except IndexError: 
     695                    field_value = None 
     696                value_list.append(field_value) 
     697                try: 
     698                    field_initial = initial[i] 
     699                except IndexError: 
     700                    field_initial = None 
     701                initial_list.append(field_initial) 
     702                takes_initial_list.append(hasattr(field, 'takes_initial')) 
     703 
     704            if not value or not [(v, i, t) for v, i, t in \ 
     705                zip(value_list, initial_list, takes_initial_list) \ 
     706                if v not in EMPTY_VALUES or t and i not in EMPTY_VALUES]: 
     707 
    682708                if self.required: 
    683709                    raise ValidationError(self.error_messages['required']) 
    684710                else: 
    685711                    return self.compress([]) 
    686712        else: 
    687713            raise ValidationError(self.error_messages['invalid']) 
     714 
    688715        for i, field in enumerate(self.fields): 
     716            field_value = value_list[i] 
     717            field_initial = initial_list[i] 
     718            takes_initial = takes_initial_list[i] 
     719            if self.required and not ( (takes_initial and field_initial not in EMPTY_VALUES) or  
     720                (field_value not in EMPTY_VALUES) ): 
     721                 raise ValidationError(self.error_messages['required']) 
    689722            try: 
    690                 field_value = value[i] 
    691             except IndexError: 
    692                 field_value = None 
    693             if self.required and field_value in EMPTY_VALUES: 
    694                 raise ValidationError(self.error_messages['required']) 
    695             try: 
    696                 clean_data.append(field.clean(field_value)) 
     723                if takes_initial: 
     724                    clean_data.append(field.clean(field_value, field_initial)) 
     725                else: 
     726                    clean_data.append(field.clean(field_value)) 
    697727            except ValidationError, e: 
    698728                # Collect all validation errors in a single list, which we'll 
    699729                # raise at the end of clean(), rather than raising a single 
     
    701731                errors.extend(e.messages) 
    702732        if errors: 
    703733            raise ValidationError(errors) 
    704         return self.compress(clean_data
     734        return self.compress(clean_data, initial_list
    705735 
    706     def compress(self, data_list): 
     736    def compress(self, data_list, initial_list=[]): 
    707737        """ 
    708738        Returns a single value for the given list of values. The values can be 
    709739        assumed to be valid. 
     
    730760        ) 
    731761        super(SplitDateTimeField, self).__init__(fields, *args, **kwargs) 
    732762 
    733     def compress(self, data_list): 
     763    def compress(self, data_list, initial_list=[]): 
    734764        if data_list: 
    735765            # Raise a validation error if time or date is empty 
    736766            # (possible if SplitDateTimeField has required=False). 
  • django/newforms/forms.py

    old new  
    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                 value = field.clean(value) 
     185                if hasattr(field, 'takes_initial'): 
     186                    initial = self.initial.get(name, field.initial) 
     187                    value = field.clean(value, initial) 
     188                else: 
     189                    value = field.clean(value) 
    186190                self.cleaned_data[name] = value 
    187191                if hasattr(self, 'clean_%s' % name): 
    188192                    value = getattr(self, 'clean_%s' % name)() 
  • tests/modeltests/model_forms/models.py

    old new  
    77most of these tests should be rewritten. 
    88""" 
    99 
     10import os 
     11import tempfile 
     12 
    1013from django.db import models 
    1114 
    1215ARTICLE_STATUS = ( 
     
    5558    def __unicode__(self): 
    5659        return self.phone 
    5760 
     61class TextFile(models.Model): 
     62    description = models.CharField(max_length=20) 
     63    file = models.FileField(upload_to=tempfile.gettempdir()) 
     64 
     65    def __unicode__(self): 
     66        return self.description 
     67 
    5868__test__ = {'API_TESTS': """ 
    5969>>> from django import newforms as forms 
    6070>>> from django.newforms.models import ModelForm 
     
    701711True 
    702712>>> f.cleaned_data 
    703713{'phone': u'312-555-1212', 'description': u'Assistance'} 
     714 
     715# FileField ################################################################### 
     716 
     717>>> class TextFileForm(ModelForm): 
     718...     class Meta: 
     719...         model = TextFile 
     720 
     721Test conditions when files is either not given or empty. 
     722 
     723>>> f = TextFileForm(data={'description': u'Assistance'}) 
     724>>> f.is_valid() 
     725False 
     726>>> f = TextFileForm(data={'description': u'Assistance'}, files={}) 
     727>>> f.is_valid() 
     728False 
     729 
     730Upload a file and ensure it all works as expected. 
     731 
     732>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test1.txt', 'content': 'hello world'}}) 
     733>>> f.is_valid() 
     734True 
     735>>> type(f.cleaned_data['file']) 
     736<class 'django.newforms.fields.UploadedFile'> 
     737>>> instance = f.save() 
     738>>> instance.file 
     739u'.../test1.txt' 
     740 
     741Edit an instance that already has the file defined in the model. This will not 
     742save the file again, but leave it exactly as it is. 
     743 
     744>>> f = TextFileForm(data={'description': u'Assistance'}, instance=instance) 
     745>>> f.is_valid() 
     746True 
     747>>> f.cleaned_data['file'] 
     748u'.../test1.txt' 
     749>>> instance = f.save() 
     750>>> instance.file 
     751u'.../test1.txt' 
     752 
     753Delete the current file since this is not done by Django. 
     754 
     755>>> os.unlink(instance.get_file_filename()) 
     756 
     757Override the file by uploading a new one. 
     758 
     759>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}, instance=instance) 
     760>>> f.is_valid() 
     761True 
     762>>> instance = f.save() 
     763>>> instance.file 
     764u'.../test2.txt' 
     765 
     766>>> instance.delete() 
     767 
     768Test the non-required FileField 
     769 
     770>>> f = TextFileForm(data={'description': u'Assistance'}) 
     771>>> f.fields['file'].required = False 
     772>>> f.is_valid() 
     773True 
     774>>> instance = f.save() 
     775>>> instance.file 
     776'' 
     777 
     778>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance) 
     779>>> f.is_valid() 
     780True 
     781>>> instance = f.save() 
     782>>> instance.file 
     783u'.../test3.txt' 
     784>>> instance.delete() 
    704785"""} 
  • tests/regressiontests/forms/extra.py

    old new  
    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

    old new  
    749749... 
    750750ValidationError: [u'This field is required.'] 
    751751 
     752>>> f.clean('', '') 
     753Traceback (most recent call last): 
     754... 
     755ValidationError: [u'This field is required.'] 
     756 
     757>>> f.clean('', 'files/test1.pdf') 
     758'files/test1.pdf' 
     759 
    752760>>> f.clean(None) 
    753761Traceback (most recent call last): 
    754762... 
    755763ValidationError: [u'This field is required.'] 
    756764 
     765>>> f.clean(None, '') 
     766Traceback (most recent call last): 
     767... 
     768ValidationError: [u'This field is required.'] 
     769 
     770>>> f.clean(None, 'files/test2.pdf') 
     771'files/test2.pdf' 
     772 
    757773>>> f.clean({}) 
    758774Traceback (most recent call last): 
    759775... 
    760776ValidationError: [u'No file was submitted.'] 
    761777 
     778>>> f.clean({}, '') 
     779Traceback (most recent call last): 
     780... 
     781ValidationError: [u'No file was submitted.'] 
     782 
     783>>> f.clean({}, 'files/test3.pdf') 
     784'files/test3.pdf' 
     785 
    762786>>> f.clean('some content that is not a file') 
    763787Traceback (most recent call last): 
    764788... 
    765789ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 
    766790 
    767 >>> f.clean({'filename': 'name', 'content':None}) 
     791>>> f.clean({'filename': 'name', 'content': None}) 
    768792Traceback (most recent call last): 
    769793... 
    770794ValidationError: [u'The submitted file is empty.'] 
    771795 
    772 >>> f.clean({'filename': 'name', 'content':''}) 
     796>>> f.clean({'filename': 'name', 'content': ''}) 
    773797Traceback (most recent call last): 
    774798... 
    775799ValidationError: [u'The submitted file is empty.'] 
    776800 
    777 >>> type(f.clean({'filename': 'name', 'content':'Some File Content'})) 
     801>>> type(f.clean({'filename': 'name', 'content': 'Some File Content'})) 
    778802<class 'django.newforms.fields.UploadedFile'> 
    779803 
     804>>> unicode(f.clean({'filename': 'name', 'content': 'Some File Content'})) 
     805u'name' 
     806 
     807>>> type(f.clean({'filename': 'name', 'content': 'Some File Content'}, 'files/test4.pdf')) 
     808<class 'django.newforms.fields.UploadedFile'> 
     809 
    780810# URLField ################################################################## 
    781811 
    782812>>> f = URLField() 
     
    11051135u'' 
    11061136>>> f.clean(None) 
    11071137u'' 
     1138>>> f = ComboField(fields=[FileField(), CharField(max_length=20)]) 
     1139>>> f.clean('data', 'some_file.txt') 
     1140Traceback (most recent call last): 
     1141...     
     1142ValidationError: [u'No file was submitted. Check the encoding type on the form.'] 
     1143>>> f.clean('', 'some_file.txt') 
     1144u'some_file.txt' 
     1145>>> f.clean('', None) 
     1146Traceback (most recent call last): 
     1147... 
     1148ValidationError: [u'This field is required.'] 
     1149>>> f.clean({'filename': 'filename.txt', 'content':'something'}) 
     1150u'filename.txt' 
     1151>>> f.clean({'filename': 'too_long_filename_for_this_combofield_here.txt', 'content':'something'}) 
     1152Traceback (most recent call last): 
     1153... 
     1154ValidationError: [u'Ensure this value has at most 20 characters (it has 46).'] 
    11081155 
     1156# MultiValueField ########################################################## 
     1157 
     1158>>> f = MultiValueField(fields=[FileField(), CharField(max_length=20)]) 
     1159>>> f.clean([{'filename': 'some_file.txt', 'content':'something'}, 'Some text']) 
     1160Traceback (most recent call last): 
     1161... 
     1162NotImplementedError: Subclasses must implement this method. 
     1163>>> f.clean([{'filename': 'some_file.txt', 'content':'something'}, 'alotoftext_more_than_20_chars']) 
     1164Traceback (most recent call last): 
     1165... 
     1166ValidationError: [u'Ensure this value has at most 20 characters (it has 29).'] 
     1167>>> f.clean([None, None]) 
     1168Traceback (most recent call last): 
     1169... 
     1170ValidationError: [u'This field is required.'] 
     1171>>> f.clean([None, 'Some text'], ['some_file.txt', None]) 
     1172Traceback (most recent call last): 
     1173... 
     1174NotImplementedError: Subclasses must implement this method. 
     1175 
    11091176# SplitDateTimeField ########################################################## 
    11101177 
    11111178>>> f = SplitDateTimeField()