87
105
# presence of self._file
88
106
if hasattr(self, '_file'):
92
110
self.storage.delete(self.name)
95
113
setattr(self.instance, self.field.name, self.name)
97
115
# Delete the filesize cache
98
116
if hasattr(self, '_size'):
118
self._committed = False
102
121
self.instance.save()
103
122
delete.alters_data = True
124
def _get_closed(self):
125
file = getattr(self, '_file', None)
126
return file is None or file.closed
127
closed = property(_get_closed)
130
file = getattr(self, '_file', None)
105
134
def __getstate__(self):
106
135
# FieldFile needs access to its associated model field and an instance
107
136
# it's attached to in order to work properly, but the only necessary
108
137
# data to be pickled is the file's name itself. Everything else will
109
138
# be restored later, by FileDescriptor below.
110
return {'_name': self.name, '_closed': False}
139
return {'name': self.name, 'closed': False, '_committed': True, '_file': None}
112
141
class FileDescriptor(object):
143
The descriptor for the file attribute on the model instance. Returns a
144
FieldFile when accessed so you can do stuff like::
146
>>> instance.file.size
148
Assigns a file object on assignment so you can do::
150
>>> instance.file = File(...)
113
153
def __init__(self, field):
114
154
self.field = field
116
156
def __get__(self, instance=None, owner=None):
117
157
if instance is None:
118
raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
158
raise AttributeError(
159
"The '%s' attribute can only be accessed from %s instances."
160
% (self.field.name, owner.__name__))
162
# This is slightly complicated, so worth an explanation.
163
# instance.file`needs to ultimately return some instance of `File`,
164
# probably a subclass. Additionally, this returned object needs to have
165
# the FieldFile API so that users can easily do things like
166
# instance.file.path and have that delegated to the file storage engine.
167
# Easy enough if we're strict about assignment in __set__, but if you
168
# peek below you can see that we're not. So depending on the current
169
# value of the field we have to dynamically construct some sort of
172
# The instance dict contains whatever was originally assigned
119
174
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.
176
# If this value is a string (instance.file = "path/to/file") or None
177
# then we simply wrap it with the appropriate attribute class according
178
# to the file field. [This is FieldFile for FileFields and
179
# ImageFieldFile for ImageFields; it's also conceivable that user
180
# subclasses might also want to subclass the attribute class]. This
181
# object understands how to convert a path to a file, and also how to
183
if isinstance(file, basestring) or file is None:
184
attr = self.field.attr_class(instance, self.field, file)
185
instance.__dict__[self.field.name] = attr
187
# Other types of files may be assigned as well, but they need to have
188
# the FieldFile interface added to the. Thus, we wrap any other type of
189
# File inside a FieldFile (well, the field's attr_class, which is
190
# usually FieldFile).
191
elif isinstance(file, File) and not isinstance(file, FieldFile):
192
file_copy = self.field.attr_class(instance, self.field, file.name)
193
file_copy.file = file
194
file_copy._committed = False
195
instance.__dict__[self.field.name] = file_copy
197
# Finally, because of the (some would say boneheaded) way pickle works,
198
# the underlying FieldFile might not actually itself have an associated
199
# file. So we need to reset the details of the FieldFile in those cases.
200
elif isinstance(file, FieldFile) and not hasattr(file, 'field'):
125
201
file.instance = instance
126
202
file.field = self.field
127
203
file.storage = self.field.storage
205
# That was fun, wasn't it?
128
206
return instance.__dict__[self.field.name]
130
208
def __set__(self, instance, value):
131
209
instance.__dict__[self.field.name] = value
133
211
class FileField(Field):
212
# The class to wrap instance attributes in. Accessing the file object off
213
# the instance will always return an instance of attr_class.
134
214
attr_class = FieldFile
216
# The descriptor to use for accessing the attribute off of the class.
217
descriptor_class = FileDescriptor
136
219
def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
137
220
for arg in ('primary_key', 'unique'):
138
221
if arg in kwargs:
162
245
return unicode(value)
247
def pre_save(self, model_instance, add):
248
"Returns field's value just before saving."
249
file = super(FileField, self).pre_save(model_instance, add)
250
if file and not file._committed:
251
# Commit the file to storage prior to saving the model
252
file.save(file.name, file, save=False)
164
255
def contribute_to_class(self, cls, name):
165
256
super(FileField, self).contribute_to_class(cls, name)
166
setattr(cls, self.name, FileDescriptor(self))
257
setattr(cls, self.name, self.descriptor_class(self))
167
258
signals.post_delete.connect(self.delete_file, sender=cls)
169
260
def delete_file(self, instance, sender, **kwargs):
188
279
return os.path.join(self.get_directory_name(), self.get_filename(filename))
190
281
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)
283
setattr(instance, self.name, data)
194
285
def formfield(self, **kwargs):
195
defaults = {'form_class': forms.FileField}
286
defaults = {'form_class': forms.FileField, 'max_length': self.max_length}
196
287
# If a file has been provided previously, then the form doesn't require
197
288
# that a new file is provided this time.
198
289
# The code to mark the form field as not required is used by
203
294
defaults.update(kwargs)
204
295
return super(FileField, self).formfield(**defaults)
297
class ImageFileDescriptor(FileDescriptor):
299
Just like the FileDescriptor, but for ImageFields. The only difference is
300
assigning the width/height to the width_field/height_field, if appropriate.
302
def __set__(self, instance, value):
303
previous_file = instance.__dict__.get(self.field.name)
304
super(ImageFileDescriptor, self).__set__(instance, value)
306
# To prevent recalculating image dimensions when we are instantiating
307
# an object from the database (bug #11084), only update dimensions if
308
# the field had a value before this assignment. Since the default
309
# value for FileField subclasses is an instance of field.attr_class,
310
# previous_file will only be None when we are called from
311
# Model.__init__(). The ImageField.update_dimension_fields method
312
# hooked up to the post_init signal handles the Model.__init__() cases.
313
# Assignment happening outside of Model.__init__() will trigger the
315
if previous_file is not None:
316
self.field.update_dimension_fields(instance, force=True)
206
318
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)
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)
217
super(ImageFieldFile, self).save(name, content, save)
219
319
def delete(self, save=True):
220
320
# Clear the image dimensions cache
221
321
if hasattr(self, '_dimensions_cache'):
225
325
class ImageField(FileField):
226
326
attr_class = ImageFieldFile
327
descriptor_class = ImageFileDescriptor
228
329
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
229
330
self.width_field, self.height_field = width_field, height_field
230
331
FileField.__init__(self, verbose_name, name, **kwargs)
333
def contribute_to_class(self, cls, name):
334
super(ImageField, self).contribute_to_class(cls, name)
335
# Attach update_dimension_fields so that dimension fields declared
336
# after their corresponding image field don't stay cleared by
337
# Model.__init__, see bug #11196.
338
signals.post_init.connect(self.update_dimension_fields, sender=cls)
340
def update_dimension_fields(self, instance, force=False, *args, **kwargs):
342
Updates field's width and height fields, if defined.
344
This method is hooked up to model's post_init signal to update
345
dimensions after instantiating a model instance. However, dimensions
346
won't be updated if the dimensions fields are already populated. This
347
avoids unnecessary recalculation when loading an object from the
350
Dimensions can be forced to update with force=True, which is how
351
ImageFileDescriptor.__set__ calls this method.
353
# Nothing to update if the field doesn't have have dimension fields.
354
has_dimension_fields = self.width_field or self.height_field
355
if not has_dimension_fields:
358
# getattr will call the ImageFileDescriptor's __get__ method, which
359
# coerces the assigned value into an instance of self.attr_class
360
# (ImageFieldFile in this case).
361
file = getattr(instance, self.attname)
363
# Nothing to update if we have no file and not being forced to update.
364
if not file and not force:
367
dimension_fields_filled = not(
368
(self.width_field and not getattr(instance, self.width_field))
369
or (self.height_field and not getattr(instance, self.height_field))
371
# When both dimension fields have values, we are most likely loading
372
# data from the database or updating an image field that already had
373
# an image stored. In the first case, we don't want to update the
374
# dimension fields because we are already getting their values from the
375
# database. In the second case, we do want to update the dimensions
376
# fields and will skip this return because force will be True since we
377
# were called from ImageFileDescriptor.__set__.
378
if dimension_fields_filled and not force:
381
# file should be an instance of ImageFieldFile or should be None.
386
# No file, so clear dimensions fields.
390
# Update the width and height fields.
392
setattr(instance, self.width_field, width)
393
if self.height_field:
394
setattr(instance, self.height_field, height)
232
396
def formfield(self, **kwargs):
233
397
defaults = {'form_class': forms.ImageField}
234
398
defaults.update(kwargs)