Source code for django.db.models.fields.files

import datetime
import os
import warnings

from django import forms
from django.core import checks
from django.core.files.base import File
from django.core.files.images import ImageFile
from import default_storage
from django.db.models import signals
from django.db.models.fields import Field
from django.utils import six
from django.utils.deprecation import RemovedInDjango110Warning
from django.utils.encoding import force_str, force_text
from django.utils.inspect import func_supports_parameter
from django.utils.translation import ugettext_lazy as _

[docs]class FieldFile(File): def __init__(self, instance, field, name): super(FieldFile, self).__init__(None, name) self.instance = instance self.field = field = self._committed = True def __eq__(self, other): # Older code may be expecting FileField values to be simple strings. # By overriding the == operator, it can remain backwards compatibility. if hasattr(other, 'name'): return == return == other def __ne__(self, other): return not self.__eq__(other) def __hash__(self): return hash( # The standard File contains most of the necessary properties, but # FieldFiles can be instantiated without a name, so that needs to # be checked for here. def _require_file(self): if not self: raise ValueError("The '%s' attribute has no file associated with it." % def _get_file(self): self._require_file() if not hasattr(self, '_file') or self._file is None: self._file =, 'rb') return self._file def _set_file(self, file): self._file = file def _del_file(self): del self._file file = property(_get_file, _set_file, _del_file) def _get_path(self): self._require_file() return path = property(_get_path) def _get_url(self): self._require_file() return url = property(_get_url) def _get_size(self): self._require_file() if not self._committed: return self.file.size return size = property(_get_size)
[docs] def open(self, mode='rb'): self._require_file()
[docs] def save(self, name, content, save=True): name = self.field.generate_filename(self.instance, name) if func_supports_parameter(, 'max_length'): =, content, max_length=self.field.max_length) else: warnings.warn( 'Backwards compatibility for storage backends without ' 'support for the `max_length` argument in ' ' will be removed in Django 1.10.', RemovedInDjango110Warning, stacklevel=2 ) =, content) setattr(self.instance,, # Update the filesize cache self._size = content.size self._committed = True # Save the object because it has changed, unless save is False if save:
save.alters_data = True
[docs] def delete(self, save=True): if not self: return # Only close the file if it's already open, which we know by the # presence of self._file if hasattr(self, '_file'): self.close() del self.file = None setattr(self.instance,, # Delete the filesize cache if hasattr(self, '_size'): del self._size self._committed = False if save:
delete.alters_data = True def _get_closed(self): file = getattr(self, '_file', None) return file is None or file.closed closed = property(_get_closed)
class FileDescriptor(object): """ The descriptor for the file attribute on the model instance. Returns a FieldFile when accessed so you can do stuff like:: >>> from myapp.models import MyModel >>> instance = MyModel.objects.get(pk=1) >>> instance.file.size Assigns a file object on assignment so you can do:: >>> with open('/path/to/', 'r') as f: ... instance.file = File(f) """ def __init__(self, field): self.field = field def __get__(self, instance=None, owner=None): if instance is None: raise AttributeError( "The '%s' attribute can only be accessed from %s instances." % (, owner.__name__)) # This is slightly complicated, so worth an explanation. # instance.file`needs to ultimately return some instance of `File`, # probably a subclass. Additionally, this returned object needs to have # the FieldFile API so that users can easily do things like # instance.file.path and have that delegated to the file storage engine. # Easy enough if we're strict about assignment in __set__, but if you # peek below you can see that we're not. So depending on the current # value of the field we have to dynamically construct some sort of # "thing" to return. # The instance dict contains whatever was originally assigned # in __set__. file = instance.__dict__[] # If this value is a string (instance.file = "path/to/file") or None # then we simply wrap it with the appropriate attribute class according # to the file field. [This is FieldFile for FileFields and # ImageFieldFile for ImageFields; it's also conceivable that user # subclasses might also want to subclass the attribute class]. This # object understands how to convert a path to a file, and also how to # handle None. if isinstance(file, six.string_types) or file is None: attr = self.field.attr_class(instance, self.field, file) instance.__dict__[] = attr # Other types of files may be assigned as well, but they need to have # the FieldFile interface added to them. Thus, we wrap any other type of # File inside a FieldFile (well, the field's attr_class, which is # usually FieldFile). elif isinstance(file, File) and not isinstance(file, FieldFile): file_copy = self.field.attr_class(instance, self.field, file_copy.file = file file_copy._committed = False instance.__dict__[] = file_copy # Finally, because of the (some would say boneheaded) way pickle works, # the underlying FieldFile might not actually itself have an associated # file. So we need to reset the details of the FieldFile in those cases. elif isinstance(file, FieldFile) and not hasattr(file, 'field'): file.instance = instance file.field = self.field = # That was fun, wasn't it? return instance.__dict__[] def __set__(self, instance, value): instance.__dict__[] = value
[docs]class FileField(Field): # The class to wrap instance attributes in. Accessing the file object off # the instance will always return an instance of attr_class. attr_class = FieldFile # The descriptor to use for accessing the attribute off of the class. descriptor_class = FileDescriptor description = _("File") def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs): self._primary_key_set_explicitly = 'primary_key' in kwargs self._unique_set_explicitly = 'unique' in kwargs = storage or default_storage self.upload_to = upload_to kwargs['max_length'] = kwargs.get('max_length', 100) super(FileField, self).__init__(verbose_name, name, **kwargs) def check(self, **kwargs): errors = super(FileField, self).check(**kwargs) errors.extend(self._check_unique()) errors.extend(self._check_primary_key()) return errors def _check_unique(self): if self._unique_set_explicitly: return [ checks.Error( "'unique' is not a valid argument for a %s." % self.__class__.__name__, hint=None, obj=self, id='fields.E200', ) ] else: return [] def _check_primary_key(self): if self._primary_key_set_explicitly: return [ checks.Error( "'primary_key' is not a valid argument for a %s." % self.__class__.__name__, hint=None, obj=self, id='fields.E201', ) ] else: return [] def deconstruct(self): name, path, args, kwargs = super(FileField, self).deconstruct() if kwargs.get("max_length") == 100: del kwargs["max_length"] kwargs['upload_to'] = self.upload_to if is not default_storage: kwargs['storage'] = return name, path, args, kwargs def get_internal_type(self): return "FileField" def get_prep_lookup(self, lookup_type, value): if hasattr(value, 'name'): value = return super(FileField, self).get_prep_lookup(lookup_type, value) def get_prep_value(self, value): "Returns field's value prepared for saving into a database." value = super(FileField, self).get_prep_value(value) # Need to convert File objects provided via a form to unicode for database insertion if value is None: return None return six.text_type(value) def pre_save(self, model_instance, add): "Returns field's value just before saving." file = super(FileField, self).pre_save(model_instance, add) if file and not file._committed: # Commit the file to storage prior to saving the model, file, save=False) return file def contribute_to_class(self, cls, name, **kwargs): super(FileField, self).contribute_to_class(cls, name, **kwargs) setattr(cls,, self.descriptor_class(self)) def get_directory_name(self): return os.path.normpath(force_text( def get_filename(self, filename): return os.path.normpath( def generate_filename(self, instance, filename): # If upload_to is a callable, make sure that the path it returns is # passed through get_valid_name() of the underlying storage. if callable(self.upload_to): directory_name, filename = os.path.split(self.upload_to(instance, filename)) filename = return os.path.normpath(os.path.join(directory_name, filename)) return os.path.join(self.get_directory_name(), self.get_filename(filename)) def save_form_data(self, instance, data): # Important: None means "no change", other false value means "clear" # This subtle distinction (rather than a more explicit marker) is # needed because we need to consume values that are also sane for a # regular (non Model-) Form to find in its cleaned_data dictionary. if data is not None: # This value will be converted to unicode and stored in the # database, so leaving False as-is is not acceptable. if not data: data = '' setattr(instance,, data) def formfield(self, **kwargs): defaults = {'form_class': forms.FileField, 'max_length': self.max_length} # If a file has been provided previously, then the form doesn't require # that a new file is provided this time. # The code to mark the form field as not required is used by # form_for_instance, but can probably be removed once form_for_instance # is gone. ModelForm uses a different method to check for an existing file. if 'initial' in kwargs: defaults['required'] = False defaults.update(kwargs) return super(FileField, self).formfield(**defaults)
Back to Top