Django

Code

root/django/trunk/django/db/models/fields/files.py

Revision 8663, 8.9 kB (checked in by jacob, 3 months ago)

FIxed #8613: removed vestigal FileField.save_file(). Thanks, kratorius.

Line 
1 import datetime
2 import os
3
4 from django.conf import settings
5 from django.db.models.fields import Field
6 from django.core.files.base import File, ContentFile
7 from django.core.files.storage import default_storage
8 from django.core.files.images import ImageFile, get_image_dimensions
9 from django.core.files.uploadedfile import UploadedFile
10 from django.utils.functional import curry
11 from django.db.models import signals
12 from django.utils.encoding import force_unicode, smart_str
13 from django.utils.translation import ugettext_lazy, ugettext as _
14 from django import forms
15 from django.db.models.loading import cache
16
17 class FieldFile(File):
18     def __init__(self, instance, field, name):
19         self.instance = instance
20         self.field = field
21         self.storage = field.storage
22         self._name = name or u''
23         self._closed = False
24
25     def __eq__(self, other):
26         # Older code may be expecting FileField values to be simple strings.
27         # By overriding the == operator, it can remain backwards compatibility.
28         if hasattr(other, 'name'):
29             return self.name == other.name
30         return self.name == other
31
32     # The standard File contains most of the necessary properties, but
33     # FieldFiles can be instantiated without a name, so that needs to
34     # be checked for here.
35
36     def _require_file(self):
37         if not self:
38             raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
39
40     def _get_file(self):
41         self._require_file()
42         if not hasattr(self, '_file'):
43             self._file = self.storage.open(self.name, 'rb')
44         return self._file
45     file = property(_get_file)
46
47     def _get_path(self):
48         self._require_file()
49         return self.storage.path(self.name)
50     path = property(_get_path)
51
52     def _get_url(self):
53         self._require_file()
54         return self.storage.url(self.name)
55     url = property(_get_url)
56
57     def _get_size(self):
58         self._require_file()
59         return self.storage.size(self.name)
60     size = property(_get_size)
61
62     def open(self, mode='rb'):
63         self._require_file()
64         return super(FieldFile, self).open(mode)
65     # open() doesn't alter the file's contents, but it does reset the pointer
66     open.alters_data = True
67
68     # In addition to the standard File API, FieldFiles have extra methods
69     # to further manipulate the underlying file, as well as update the
70     # associated model instance.
71
72     def save(self, name, content, save=True):
73         name = self.field.generate_filename(self.instance, name)
74         self._name = self.storage.save(name, content)
75         setattr(self.instance, self.field.name, self.name)
76
77         # Update the filesize cache
78         self._size = len(content)
79
80         # Save the object because it has changed, unless save is False
81         if save:
82             self.instance.save()
83     save.alters_data = True
84
85     def delete(self, save=True):
86         # Only close the file if it's already open, which we know by the
87         # presence of self._file
88         if hasattr(self, '_file'):
89             self.close()
90             del self._file
91            
92         self.storage.delete(self.name)
93
94         self._name = None
95         setattr(self.instance, self.field.name, self.name)
96
97         # Delete the filesize cache
98         if hasattr(self, '_size'):
99             del self._size
100
101         if save:
102             self.instance.save()
103     delete.alters_data = True
104
105     def __getstate__(self):
106         # FieldFile needs access to its associated model field and an instance
107         # it's attached to in order to work properly, but the only necessary
108         # data to be pickled is the file's name itself. Everything else will
109         # be restored later, by FileDescriptor below.
110         return {'_name': self.name, '_closed': False}
111
112 class FileDescriptor(object):
113     def __init__(self, field):
114         self.field = field
115
116     def __get__(self, instance=None, owner=None):
117         if instance is None:
118             raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
119         file = instance.__dict__[self.field.name]
120         if not isinstance(file, FieldFile):
121             # Create a new instance of FieldFile, based on a given file name
122             instance.__dict__[self.field.name] = self.field.attr_class(instance, self.field, file)
123         elif not hasattr(file, 'field'):
124             # The FieldFile was pickled, so some attributes need to be reset.
125             file.instance = instance
126             file.field = self.field
127             file.storage = self.field.storage
128         return instance.__dict__[self.field.name]
129
130     def __set__(self, instance, value):
131         instance.__dict__[self.field.name] = value
132
133 class FileField(Field):
134     attr_class = FieldFile
135
136     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
137         for arg in ('primary_key', 'unique'):
138             if arg in kwargs:
139                 raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__))
140
141         self.storage = storage or default_storage
142         self.upload_to = upload_to
143         if callable(upload_to):
144             self.generate_filename = upload_to
145
146         kwargs['max_length'] = kwargs.get('max_length', 100)
147         super(FileField, self).__init__(verbose_name, name, **kwargs)
148
149     def get_internal_type(self):
150         return "FileField"
151
152     def get_db_prep_lookup(self, lookup_type, value):
153         if hasattr(value, 'name'):
154             value = value.name
155         return super(FileField, self).get_db_prep_lookup(lookup_type, value)
156
157     def get_db_prep_value(self, value):
158         "Returns field's value prepared for saving into a database."
159         # Need to convert File objects provided via a form to unicode for database insertion
160         if value is None:
161             return None
162         return unicode(value)
163
164     def contribute_to_class(self, cls, name):
165         super(FileField, self).contribute_to_class(cls, name)
166         setattr(cls, self.name, FileDescriptor(self))
167         signals.post_delete.connect(self.delete_file, sender=cls)
168
169     def delete_file(self, instance, sender, **kwargs):
170         file = getattr(instance, self.attname)
171         # If no other object of this type references the file,
172         # and it's not the default value for future objects,
173         # delete it from the backend.
174         if file and file.name != self.default and \
175             not sender._default_manager.filter(**{self.name: file.name}):
176                 file.delete(save=False)
177         elif file:
178             # Otherwise, just close the file, so it doesn't tie up resources.
179             file.close()
180
181     def get_directory_name(self):
182         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
183
184     def get_filename(self, filename):
185         return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))
186
187     def generate_filename(self, instance, filename):
188         return os.path.join(self.get_directory_name(), self.get_filename(filename))
189
190     def save_form_data(self, instance, data):
191         if data and isinstance(data, UploadedFile):
192             getattr(instance, self.name).save(data.name, data, save=False)
193
194     def formfield(self, **kwargs):
195         defaults = {'form_class': forms.FileField}
196         # If a file has been provided previously, then the form doesn't require
197         # that a new file is provided this time.
198         # The code to mark the form field as not required is used by
199         # form_for_instance, but can probably be removed once form_for_instance
200         # is gone. ModelForm uses a different method to check for an existing file.
201         if 'initial' in kwargs:
202             defaults['required'] = False
203         defaults.update(kwargs)
204         return super(FileField, self).formfield(**defaults)
205
206 class ImageFieldFile(ImageFile, FieldFile):
207     def save(self, name, content, save=True):
208         # Repopulate the image dimension cache.
209         self._dimensions_cache = get_image_dimensions(content)
210
211         # Update width/height fields, if needed
212         if self.field.width_field:
213             setattr(self.instance, self.field.width_field, self.width)
214         if self.field.height_field:
215             setattr(self.instance, self.field.height_field, self.height)
216
217         super(ImageFieldFile, self).save(name, content, save)
218
219     def delete(self, save=True):
220         # Clear the image dimensions cache
221         if hasattr(self, '_dimensions_cache'):
222             del self._dimensions_cache
223         super(ImageFieldFile, self).delete(save)
224
225 class ImageField(FileField):
226     attr_class = ImageFieldFile
227
228     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
229         self.width_field, self.height_field = width_field, height_field
230         FileField.__init__(self, verbose_name, name, **kwargs)
231
232     def formfield(self, **kwargs):
233         defaults = {'form_class': forms.ImageField}
234         defaults.update(kwargs)
235         return super(ImageField, self).formfield(**defaults)
Note: See TracBrowser for help on using the browser.