Code

Ticket #2534: filefield_core_fixes_6635.diff

File filefield_core_fixes_6635.diff, 6.6 KB (added by james.utter@…, 6 years ago)

updated patch to point to oldforms instead of forms

Line 
1Index: db/models/manipulators.py
2===================================================================
3--- db/models/manipulators.py   (revision 6635)
4+++ db/models/manipulators.py   (working copy)
5@@ -170,9 +170,9 @@
6                                 pass
7 
8                     for f in related.opts.fields:
9-                        if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
10+                        if f.core and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
11                             all_cores_given = False
12-                        elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
13+                        elif f.core and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
14                             all_cores_blank = False
15                         # If this field isn't editable, give it the same value it had
16                         # previously, according to the given ID. If the ID wasn't
17Index: db/models/fields/__init__.py
18===================================================================
19--- db/models/fields/__init__.py        (revision 6635)
20+++ db/models/fields/__init__.py        (working copy)
21@@ -706,10 +706,12 @@
22         return super(EmailField, self).formfield(**defaults)
23 
24 class FileField(Field):
25-    def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
26+    def __init__(self, verbose_name=None, name=None, upload_to='', can_delete=False, **kwargs):
27         self.upload_to = upload_to
28+        self.can_delete = can_delete
29         kwargs['max_length'] = kwargs.get('max_length', 100)       
30         Field.__init__(self, verbose_name, name, **kwargs)
31+        assert (not self.can_delete) or self.null, "A FileField must have null=True if can_delete=True"
32 
33     def get_db_prep_save(self, value):
34         "Returns field's value prepared for saving into a database."
35@@ -718,6 +720,23 @@
36             return None
37         return unicode(value)
38 
39+    def get_manipulator_new_data(self, new_data, rel=False):
40+        # Return the value which the field will take, if it is saved.
41+        # This will be either the pathname of the uploaded file, or the
42+        # pathname of the existing file, if no file was uploaded.
43+        if rel:
44+            extract = lambda field: (new_data.get("%s%s" % (self.name, field), [None])[0])
45+        else:
46+            extract = lambda field: (new_data.get("%s%s" % (self.name, field), None))
47+        if extract('_delete'):
48+            return None
49+        try:
50+            file = extract('_file')
51+            return self.get_filename(file['filename'])
52+        except (IndexError, KeyError, TypeError):
53+            pass
54+        return extract('')
55+
56     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
57         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
58         if not self.blank:
59@@ -772,14 +791,29 @@
60                 os.remove(file_name)
61 
62     def get_manipulator_field_objs(self):
63+        if self.can_delete:
64+            return [oldforms.FileUploadField, oldforms.HiddenField, oldforms.LabeledCheckbox]
65         return [oldforms.FileUploadField, oldforms.HiddenField]
66 
67     def get_manipulator_field_names(self, name_prefix):
68+        if self.can_delete:
69+            return [name_prefix + self.name + '_file', name_prefix + self.name, name_prefix + self.name + '_delete']
70         return [name_prefix + self.name + '_file', name_prefix + self.name]
71 
72     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
73-        upload_field_name = self.get_manipulator_field_names('')[0]
74-        if new_data.get(upload_field_name, False):
75+        field_names = self.get_manipulator_field_names('')
76+        upload_field_name = field_names[0]
77+        # Delete only if delete checkbox is present and checked
78+        if self.can_delete and ((rel and new_data.get(field_names[2], [False])[0]) or
79+                                (not rel and new_data.get(field_names[2], False))):
80+            file_name = getattr(original_object, 'get_%s_filename' % self.name)()
81+            # If file exists, delete it
82+            if file_name and os.path.exists(file_name):
83+                os.remove(file_name)
84+            setattr(new_object, self.name, None)
85+            new_object.save()
86+        elif new_data.get(upload_field_name, False):
87+
88             func = getattr(new_object, 'save_%s_file' % self.name)
89             if rel:
90                 func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)
91@@ -833,6 +867,8 @@
92         FileField.__init__(self, verbose_name, name, **kwargs)
93 
94     def get_manipulator_field_objs(self):
95+        if self.can_delete:
96+            return [oldforms.ImageUploadField, oldforms.HiddenField, oldforms.LabeledCheckbox]
97         return [oldforms.ImageUploadField, oldforms.HiddenField]
98 
99     def contribute_to_class(self, cls, name):
100Index: oldforms/__init__.py
101===================================================================
102--- oldforms/__init__.py        (revision 6635)
103+++ oldforms/__init__.py        (working copy)
104@@ -1048,3 +1048,31 @@
105             v(field_data, all_data)
106         except validators.ValidationError, e:
107             raise validators.CriticalValidationError, e.messages
108+
109+
110+class LabeledCheckbox(CheckboxField):
111+    """
112+    A checkbox for which is_required is allowed to be set to False, and
113+    which optionally displays a text label before its form control.
114+    """
115+    def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False, label=None, max_length=None):
116+        self.label = label
117+        # This default is to support using this checkbox as a delete field in
118+        # FileField objects. It is only required because there is no easy way
119+        # to send unique constructor parameters to multiple manipulator field
120+        # objects.
121+        if self.label == None:
122+            self.label = ugettext("Delete")
123+        super(LabeledCheckbox, self).__init__(field_name, checked_by_default, validator_list)
124+
125+    def render(self, data):
126+        checked_html = ''
127+        if data or (data is '' and self.checked_by_default):
128+            checked_html = ' checked="checked"'
129+        label_html = ''
130+        if self.label:
131+            label_html = " %s " % self.label
132+        return '%s<input type="checkbox" id="%s" class="v%s" name="%s"%s value="on" />' % \
133+            (label_html, self.get_id(), self.__class__.__name__,
134+            self.field_name, checked_html)
135+