Code

Ticket #10044: 10044.2.diff

File 10044.2.diff, 5.3 KB (added by Gulopine, 5 years ago)

Removed the save_form_data() override in FileField, now that the standard behavior is sufficient for populating a model from a form (and delaying the save is better typically, anyway)

Line 
1Index: django/db/models/fields/files.py
2===================================================================
3--- django/db/models/fields/files.py    (revision 9756)
4+++ django/db/models/fields/files.py    (working copy)
5@@ -1,3 +1,4 @@
6+import copy
7 import datetime
8 import os
9 
10@@ -21,6 +22,7 @@
11         self.storage = field.storage
12         self._name = name or u''
13         self._closed = False
14+        self._committed = True
15 
16     def __eq__(self, other):
17         # Older code may be expecting FileField values to be simple strings.
18@@ -79,6 +81,7 @@
19 
20         # Update the filesize cache
21         self._size = len(content)
22+        self._committed = True
23 
24         # Save the object because it has changed, unless save is False
25         if save:
26@@ -100,6 +103,7 @@
27         # Delete the filesize cache
28         if hasattr(self, '_size'):
29             del self._size
30+        self._committed = False
31 
32         if save:
33             self.instance.save()
34@@ -110,7 +114,7 @@
35         # it's attached to in order to work properly, but the only necessary
36         # data to be pickled is the file's name itself. Everything else will
37         # be restored later, by FileDescriptor below.
38-        return {'_name': self.name, '_closed': False}
39+        return {'_name': self.name, '_closed': False, '_committed': True}
40 
41 class FileDescriptor(object):
42     def __init__(self, field):
43@@ -120,10 +124,21 @@
44         if instance is None:
45             raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
46         file = instance.__dict__[self.field.name]
47-        if not isinstance(file, FieldFile):
48+        if isinstance(file, basestring) or file is None:
49             # Create a new instance of FieldFile, based on a given file name
50             instance.__dict__[self.field.name] = self.field.attr_class(instance, self.field, file)
51-        elif not hasattr(file, 'field'):
52+        elif isinstance(file, File) and not isinstance(file, FieldFile):
53+            # Other types of files may be assigned as well, but they need to
54+            # have the FieldFile interface added to them
55+            file_copy = copy.copy(file)
56+            file_copy.__class__ = type(file.__class__.__name__,
57+                                       (file.__class__, FieldFile), {})
58+            file_copy.instance = instance
59+            file_copy.field = self.field
60+            file_copy.storage = self.field.storage
61+            file_copy._committed = False
62+            instance.__dict__[self.field.name] = file_copy
63+        elif isinstance(file, FieldFile) and not hasattr(file, 'field'):
64             # The FieldFile was pickled, so some attributes need to be reset.
65             file.instance = instance
66             file.field = self.field
67@@ -164,6 +179,14 @@
68             return None
69         return unicode(value)
70 
71+    def pre_save(self, model_instance, add):
72+        "Returns field's value just before saving."
73+        file = super(FileField, self).pre_save(model_instance, add)
74+        if file and not file._committed:
75+            # Commit the file to storage prior to saving the model
76+            file.save(file.name, file, save=False)
77+        return file
78+
79     def contribute_to_class(self, cls, name):
80         super(FileField, self).contribute_to_class(cls, name)
81         setattr(cls, self.name, FileDescriptor(self))
82@@ -190,10 +213,6 @@
83     def generate_filename(self, instance, filename):
84         return os.path.join(self.get_directory_name(), self.get_filename(filename))
85 
86-    def save_form_data(self, instance, data):
87-        if data and isinstance(data, UploadedFile):
88-            getattr(instance, self.name).save(data.name, data, save=False)
89-
90     def formfield(self, **kwargs):
91         defaults = {'form_class': forms.FileField}
92         # If a file has been provided previously, then the form doesn't require
93Index: django/core/files/base.py
94===================================================================
95--- django/core/files/base.py   (revision 9756)
96+++ django/core/files/base.py   (working copy)
97@@ -32,6 +32,8 @@
98         return self.size
99 
100     def _get_name(self):
101+        if not hasattr(self, '_name'):
102+            raise ValueError("This operation requires the file to have a name.")
103         return self._name
104     name = property(_get_name)
105 
106Index: tests/modeltests/files/models.py
107===================================================================
108--- tests/modeltests/files/models.py    (revision 9756)
109+++ tests/modeltests/files/models.py    (working copy)
110@@ -9,6 +9,7 @@
111 import tempfile
112 from django.db import models
113 from django.core.files.base import ContentFile
114+from django.core.files.uploadedfile import SimpleUploadedFile
115 from django.core.files.storage import FileSystemStorage
116 from django.core.cache import cache
117 
118@@ -54,6 +55,16 @@
119 >>> obj1.normal.read()
120 'content'
121 
122+# File objects can be assigned to FileField attributes,  but shouldn't get
123+# committed until the model it's attached to is saved.
124+
125+>>> obj1.normal = SimpleUploadedFile('assignment.txt', 'content')
126+>>> temp_storage.listdir('tests')
127+([], [u'default.txt', u'django_test.txt'])
128+>>> obj1.save()
129+>>> temp_storage.listdir('tests')
130+([], [u'assignment.txt', u'default.txt', u'django_test.txt'])
131+
132 # Files can be read in a little at a time, if necessary.
133 
134 >>> obj1.normal.open()