Ticket #25756: multiple.py

File multiple.py, 4.5 KB (added by Riccardo Di Virgilio, 9 years ago)
Line 
1# -*- coding: utf-8 -*-
2
3from django import forms
4from django.contrib.postgres.fields import ArrayField
5from django.contrib.postgres.forms import SplitArrayField
6from django.core.exceptions import ValidationError
7from django.db.models.fields.files import FieldFile, File
8from django.utils import six
9from django.utils.translation import ugettext_lazy as _
10
11class MultiFileInput(forms.FileInput):
12
13 def render(self, name, value, attrs={}):
14 attrs['multiple'] = 'multiple'
15 return super(MultiFileInput, self).render(name, None, attrs=attrs)
16
17 def value_from_datadict(self, data, files, name):
18 if hasattr(files, 'getlist'):
19 return files.getlist(name)
20 else:
21 return [files.get(name)]
22
23class MultiFileField(forms.FileField):
24 widget = MultiFileInput
25 default_error_messages = {
26 'min_num': u"Ensure at least %(min_num)s files are uploaded (received %(num_files)s).",
27 'max_num': u"Ensure at most %(max_num)s files are uploaded (received %(num_files)s).",
28 'file_size' : u"File: %(uploaded_file_name)s, exceeded maximum upload size."
29 }
30
31 def __init__(self, *args, **kwargs):
32 self.min_num = kwargs.pop('min_num', 0)
33 self.max_num = kwargs.pop('max_num', None)
34 self.maximum_file_size = kwargs.pop('maximum_file_size', None)
35 super(MultiFileField, self).__init__(*args, **kwargs)
36
37 def to_python(self, data):
38 ret = []
39 for item in data:
40 ret.append(super(MultiFileField, self).to_python(item))
41 return ret
42
43 def validate(self, data):
44 super(MultiFileField, self).validate(data)
45 num_files = len(data)
46 if len(data) and not data[0]:
47 num_files = 0
48 if num_files < self.min_num:
49 raise ValidationError(self.error_messages['min_num'] % {'min_num': self.min_num, 'num_files': num_files})
50 return
51 elif self.max_num and num_files > self.max_num:
52 raise ValidationError(self.error_messages['max_num'] % {'max_num': self.max_num, 'num_files': num_files})
53 for uploaded_file in data:
54 if self.maximum_file_size and uploaded_file.size > self.maximum_file_size:
55 raise ValidationError(self.error_messages['file_size'] % {'uploaded_file_name': uploaded_file.name})
56
57def to_file_object(field, instance, file):
58 if isinstance(file, six.string_types) or file is None:
59 return field.attr_class(instance, field, file)
60 elif isinstance(file, File) and not isinstance(file, FieldFile):
61 file_copy = field.attr_class(instance, field, file.name)
62 file_copy.file = file
63 file_copy._committed = False
64 return file_copy
65 elif isinstance(file, FieldFile) and not hasattr(file, 'field'):
66 file.instance = instance
67 file.field = field
68 file.storage = field.storage
69 return file
70 else:
71 return file
72
73class ArrayFileDescriptor(object):
74
75 def __init__(self, field):
76 self.field = field
77
78 def __get__(self, instance=None, owner=None):
79 if instance is None:
80 raise AttributeError(
81 "The '%s' attribute can only be accessed from %s instances."
82 % (self.field.name, owner.__name__))
83
84 return [
85 to_file_object(self.field.base_field, instance, file)
86 for file in instance.__dict__[self.field.name]
87 ]
88
89 def __set__(self, instance, value):
90 instance.__dict__[self.field.name] = value
91
92class ArrayFileField(ArrayField):
93
94 descriptor_class = ArrayFileDescriptor
95
96 def set_attributes_from_name(self, name):
97 super(ArrayField, self).set_attributes_from_name(name)
98 self.base_field.set_attributes_from_name("%s_array" % name)
99
100 def contribute_to_class(self, cls, name, **kwargs):
101 super(ArrayFileField, self).contribute_to_class(cls, name, **kwargs)
102 setattr(cls, self.name, self.descriptor_class(self))
103
104 def pre_save(self, instance, add):
105 "Returns field's value just before saving."
106 files = [
107 to_file_object(self.base_field, instance, file)
108 for file in super(ArrayField, self).pre_save(instance, add)
109 ]
110
111 for file_copy in files:
112 if file_copy and not file_copy._committed:
113 file_copy.save(file_copy.name, file_copy, save=False)
114
115 return files
116
117 def formfield(self, **kwargs):
118 defaults = {
119 'form_class': MultiFileField,
120 'max_num': self.size
121 }
122 defaults.update(kwargs)
123 return super(ArrayField, self).formfield(**defaults)
Back to Top