Code

Ticket #14039: 14039_1.4.3_with_note.diff

File 14039_1.4.3_with_note.diff, 4.5 KB (added by bradleyayers, 16 months ago)

Updated to apply cleanly to 1.4.3

Line 
1diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
2index 527a3c0..c7cb731 100644
3--- a/django/db/models/fields/__init__.py
4+++ b/django/db/models/fields/__init__.py
5@@ -66,6 +66,8 @@ class Field(object):
6                     u'already exists.'),
7     }
8 
9+    defer_save = False  # If true, defer saving until after other fields.
10+
11     # Generic field type description, usually overriden by subclasses
12     def _description(self):
13         return _(u'Field of type: %(field_type)s') % {
14diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
15index 6f1f183..ae4d526 100644
16--- a/django/db/models/fields/files.py
17+++ b/django/db/models/fields/files.py
18@@ -203,6 +203,7 @@ class FileDescriptor(object):
19     def __set__(self, instance, value):
20         instance.__dict__[self.field.name] = value
21 
22+
23 class FileField(Field):
24     # The class to wrap instance attributes in. Accessing the file object off
25     # the instance will always return an instance of attr_class.
26@@ -211,6 +212,8 @@ class FileField(Field):
27     # The descriptor to use for accessing the attribute off of the class.
28     descriptor_class = FileDescriptor
29 
30+    defer_save = True
31+
32     description = _("File")
33 
34     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
35diff --git a/django/forms/fields.py b/django/forms/fields.py
36index 16007e5..21ac628 100644
37--- a/django/forms/fields.py
38+++ b/django/forms/fields.py
39@@ -51,6 +51,9 @@ class Field(object):
40         'invalid': _(u'Enter a valid value.'),
41     }
42 
43+    # If true, clean() takes initial value as well as data.
44+    clean_takes_initial = False
45+
46     # Tracks each time a Field instance is created. Used to retain order.
47     creation_counter = 0
48 
49@@ -489,6 +492,8 @@ class FileField(Field):
50         'contradiction': _(u'Please either submit a file or check the clear checkbox, not both.')
51     }
52 
53+    clean_takes_initial = True
54+
55     def __init__(self, *args, **kwargs):
56         self.max_length = kwargs.pop('max_length', None)
57         self.allow_empty_file = kwargs.pop('allow_empty_file', False)
58@@ -843,6 +848,13 @@ class MultiValueField(Field):
59     "compressed" version of those values -- a single value.
60 
61     You'll probably want to use this with MultiWidget.
62+
63+    If your MultiValueField subclass includes a FileField or derivative as one
64+    of its fields, there will be subtle differences in behavior (particularly
65+    displaying a bound form with initial data) unless you set your subclasses'
66+    ``clean_takes_initial`` attribute to True and override the clean() method
67+    to accept an ``initial`` arg and pass it (or an appropriate part of it) on
68+    to the member FileField.
69     """
70     default_error_messages = {
71         'invalid': _(u'Enter a list of values.'),
72diff --git a/django/forms/forms.py b/django/forms/forms.py
73index 94eb22d..61d11d1 100644
74--- a/django/forms/forms.py
75+++ b/django/forms/forms.py
76@@ -280,7 +280,7 @@ class BaseForm(StrAndUnicode):
77             # widgets split data over several HTML fields.
78             value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
79             try:
80-                if isinstance(field, FileField):
81+                if field.clean_takes_initial:
82                     initial = self.initial.get(name, field.initial)
83                     value = field.clean(value, initial)
84                 else:
85diff --git a/django/forms/models.py b/django/forms/models.py
86index cd8f027..d37d393 100644
87--- a/django/forms/models.py
88+++ b/django/forms/models.py
89@@ -34,7 +34,7 @@ def construct_instance(form, instance, fields=None, exclude=None):
90     opts = instance._meta
91 
92     cleaned_data = form.cleaned_data
93-    file_field_list = []
94+    deferred_list = []
95     for f in opts.fields:
96         if not f.editable or isinstance(f, models.AutoField) \
97                 or not f.name in cleaned_data:
98@@ -45,12 +45,12 @@ def construct_instance(form, instance, fields=None, exclude=None):
99             continue
100         # Defer saving file-type fields until after the other fields, so a
101         # callable upload_to can use the values from other fields.
102-        if isinstance(f, models.FileField):
103-            file_field_list.append(f)
104+        if f.defer_save:
105+            deferred_list.append(f)
106         else:
107             f.save_form_data(instance, cleaned_data[f.name])
108 
109-    for f in file_field_list:
110+    for f in deferred_list:
111         f.save_form_data(instance, cleaned_data[f.name])
112 
113     return instance