web/lib/django/db/models/fields/files.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 import copy
       
     2 import datetime
       
     3 import os
       
     4 
       
     5 from django.conf import settings
       
     6 from django.db.models.fields import Field
       
     7 from django.core.files.base import File, ContentFile
       
     8 from django.core.files.storage import default_storage
       
     9 from django.core.files.images import ImageFile, get_image_dimensions
       
    10 from django.core.files.uploadedfile import UploadedFile
       
    11 from django.utils.functional import curry
       
    12 from django.db.models import signals
       
    13 from django.utils.encoding import force_unicode, smart_str
       
    14 from django.utils.translation import ugettext_lazy, ugettext as _
       
    15 from django import forms
       
    16 from django.db.models.loading import cache
       
    17 
       
    18 class FieldFile(File):
       
    19     def __init__(self, instance, field, name):
       
    20         super(FieldFile, self).__init__(None, name)
       
    21         self.instance = instance
       
    22         self.field = field
       
    23         self.storage = field.storage
       
    24         self._committed = True
       
    25 
       
    26     def __eq__(self, other):
       
    27         # Older code may be expecting FileField values to be simple strings.
       
    28         # By overriding the == operator, it can remain backwards compatibility.
       
    29         if hasattr(other, 'name'):
       
    30             return self.name == other.name
       
    31         return self.name == other
       
    32 
       
    33     def __ne__(self, other):
       
    34         return not self.__eq__(other)
       
    35 
       
    36     def __hash__(self):
       
    37         # Required because we defined a custom __eq__.
       
    38         return hash(self.name)
       
    39 
       
    40     # The standard File contains most of the necessary properties, but
       
    41     # FieldFiles can be instantiated without a name, so that needs to
       
    42     # be checked for here.
       
    43 
       
    44     def _require_file(self):
       
    45         if not self:
       
    46             raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
       
    47 
       
    48     def _get_file(self):
       
    49         self._require_file()
       
    50         if not hasattr(self, '_file') or self._file is None:
       
    51             self._file = self.storage.open(self.name, 'rb')
       
    52         return self._file
       
    53 
       
    54     def _set_file(self, file):
       
    55         self._file = file
       
    56 
       
    57     def _del_file(self):
       
    58         del self._file
       
    59 
       
    60     file = property(_get_file, _set_file, _del_file)
       
    61 
       
    62     def _get_path(self):
       
    63         self._require_file()
       
    64         return self.storage.path(self.name)
       
    65     path = property(_get_path)
       
    66 
       
    67     def _get_url(self):
       
    68         self._require_file()
       
    69         return self.storage.url(self.name)
       
    70     url = property(_get_url)
       
    71 
       
    72     def _get_size(self):
       
    73         self._require_file()
       
    74         if not self._committed:
       
    75             return len(self.file)
       
    76         return self.storage.size(self.name)
       
    77     size = property(_get_size)
       
    78 
       
    79     def open(self, mode='rb'):
       
    80         self._require_file()
       
    81         self.file.open(mode)
       
    82     # open() doesn't alter the file's contents, but it does reset the pointer
       
    83     open.alters_data = True
       
    84 
       
    85     # In addition to the standard File API, FieldFiles have extra methods
       
    86     # to further manipulate the underlying file, as well as update the
       
    87     # associated model instance.
       
    88 
       
    89     def save(self, name, content, save=True):
       
    90         name = self.field.generate_filename(self.instance, name)
       
    91         self.name = self.storage.save(name, content)
       
    92         setattr(self.instance, self.field.name, self.name)
       
    93 
       
    94         # Update the filesize cache
       
    95         self._size = len(content)
       
    96         self._committed = True
       
    97 
       
    98         # Save the object because it has changed, unless save is False
       
    99         if save:
       
   100             self.instance.save()
       
   101     save.alters_data = True
       
   102 
       
   103     def delete(self, save=True):
       
   104         # Only close the file if it's already open, which we know by the
       
   105         # presence of self._file
       
   106         if hasattr(self, '_file'):
       
   107             self.close()
       
   108             del self.file
       
   109 
       
   110         self.storage.delete(self.name)
       
   111 
       
   112         self.name = None
       
   113         setattr(self.instance, self.field.name, self.name)
       
   114 
       
   115         # Delete the filesize cache
       
   116         if hasattr(self, '_size'):
       
   117             del self._size
       
   118         self._committed = False
       
   119 
       
   120         if save:
       
   121             self.instance.save()
       
   122     delete.alters_data = True
       
   123 
       
   124     def _get_closed(self):
       
   125         file = getattr(self, '_file', None)
       
   126         return file is None or file.closed
       
   127     closed = property(_get_closed)
       
   128 
       
   129     def close(self):
       
   130         file = getattr(self, '_file', None)
       
   131         if file is not None:
       
   132             file.close()
       
   133 
       
   134     def __getstate__(self):
       
   135         # FieldFile needs access to its associated model field and an instance
       
   136         # it's attached to in order to work properly, but the only necessary
       
   137         # data to be pickled is the file's name itself. Everything else will
       
   138         # be restored later, by FileDescriptor below.
       
   139         return {'name': self.name, 'closed': False, '_committed': True, '_file': None}
       
   140 
       
   141 class FileDescriptor(object):
       
   142     """
       
   143     The descriptor for the file attribute on the model instance. Returns a
       
   144     FieldFile when accessed so you can do stuff like::
       
   145 
       
   146         >>> instance.file.size
       
   147 
       
   148     Assigns a file object on assignment so you can do::
       
   149 
       
   150         >>> instance.file = File(...)
       
   151 
       
   152     """
       
   153     def __init__(self, field):
       
   154         self.field = field
       
   155 
       
   156     def __get__(self, instance=None, owner=None):
       
   157         if instance is None:
       
   158             raise AttributeError(
       
   159                 "The '%s' attribute can only be accessed from %s instances."
       
   160                 % (self.field.name, owner.__name__))
       
   161 
       
   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
       
   170         # "thing" to return.
       
   171 
       
   172         # The instance dict contains whatever was originally assigned
       
   173         # in __set__.
       
   174         file = instance.__dict__[self.field.name]
       
   175 
       
   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
       
   182         # handle None.
       
   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
       
   186 
       
   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
       
   196 
       
   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'):
       
   201             file.instance = instance
       
   202             file.field = self.field
       
   203             file.storage = self.field.storage
       
   204 
       
   205         # That was fun, wasn't it?
       
   206         return instance.__dict__[self.field.name]
       
   207 
       
   208     def __set__(self, instance, value):
       
   209         instance.__dict__[self.field.name] = value
       
   210 
       
   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.
       
   214     attr_class = FieldFile
       
   215 
       
   216     # The descriptor to use for accessing the attribute off of the class.
       
   217     descriptor_class = FileDescriptor
       
   218 
       
   219     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
       
   220         for arg in ('primary_key', 'unique'):
       
   221             if arg in kwargs:
       
   222                 raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__))
       
   223 
       
   224         self.storage = storage or default_storage
       
   225         self.upload_to = upload_to
       
   226         if callable(upload_to):
       
   227             self.generate_filename = upload_to
       
   228 
       
   229         kwargs['max_length'] = kwargs.get('max_length', 100)
       
   230         super(FileField, self).__init__(verbose_name, name, **kwargs)
       
   231 
       
   232     def get_internal_type(self):
       
   233         return "FileField"
       
   234 
       
   235     def get_db_prep_lookup(self, lookup_type, value):
       
   236         if hasattr(value, 'name'):
       
   237             value = value.name
       
   238         return super(FileField, self).get_db_prep_lookup(lookup_type, value)
       
   239 
       
   240     def get_db_prep_value(self, value):
       
   241         "Returns field's value prepared for saving into a database."
       
   242         # Need to convert File objects provided via a form to unicode for database insertion
       
   243         if value is None:
       
   244             return None
       
   245         return unicode(value)
       
   246 
       
   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)
       
   253         return file
       
   254 
       
   255     def contribute_to_class(self, cls, name):
       
   256         super(FileField, self).contribute_to_class(cls, name)
       
   257         setattr(cls, self.name, self.descriptor_class(self))
       
   258         signals.post_delete.connect(self.delete_file, sender=cls)
       
   259 
       
   260     def delete_file(self, instance, sender, **kwargs):
       
   261         file = getattr(instance, self.attname)
       
   262         # If no other object of this type references the file,
       
   263         # and it's not the default value for future objects,
       
   264         # delete it from the backend.
       
   265         if file and file.name != self.default and \
       
   266             not sender._default_manager.filter(**{self.name: file.name}):
       
   267                 file.delete(save=False)
       
   268         elif file:
       
   269             # Otherwise, just close the file, so it doesn't tie up resources.
       
   270             file.close()
       
   271 
       
   272     def get_directory_name(self):
       
   273         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
       
   274 
       
   275     def get_filename(self, filename):
       
   276         return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))
       
   277 
       
   278     def generate_filename(self, instance, filename):
       
   279         return os.path.join(self.get_directory_name(), self.get_filename(filename))
       
   280 
       
   281     def save_form_data(self, instance, data):
       
   282         if data:
       
   283             setattr(instance, self.name, data)
       
   284 
       
   285     def formfield(self, **kwargs):
       
   286         defaults = {'form_class': forms.FileField, 'max_length': self.max_length}
       
   287         # If a file has been provided previously, then the form doesn't require
       
   288         # that a new file is provided this time.
       
   289         # The code to mark the form field as not required is used by
       
   290         # form_for_instance, but can probably be removed once form_for_instance
       
   291         # is gone. ModelForm uses a different method to check for an existing file.
       
   292         if 'initial' in kwargs:
       
   293             defaults['required'] = False
       
   294         defaults.update(kwargs)
       
   295         return super(FileField, self).formfield(**defaults)
       
   296 
       
   297 class ImageFileDescriptor(FileDescriptor):
       
   298     """
       
   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.
       
   301     """
       
   302     def __set__(self, instance, value):
       
   303         previous_file = instance.__dict__.get(self.field.name)
       
   304         super(ImageFileDescriptor, self).__set__(instance, value)
       
   305 
       
   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
       
   314         # update right here.
       
   315         if previous_file is not None:
       
   316             self.field.update_dimension_fields(instance, force=True)
       
   317 
       
   318 class ImageFieldFile(ImageFile, FieldFile):
       
   319     def delete(self, save=True):
       
   320         # Clear the image dimensions cache
       
   321         if hasattr(self, '_dimensions_cache'):
       
   322             del self._dimensions_cache
       
   323         super(ImageFieldFile, self).delete(save)
       
   324 
       
   325 class ImageField(FileField):
       
   326     attr_class = ImageFieldFile
       
   327     descriptor_class = ImageFileDescriptor
       
   328 
       
   329     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
       
   330         self.width_field, self.height_field = width_field, height_field
       
   331         FileField.__init__(self, verbose_name, name, **kwargs)
       
   332 
       
   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)
       
   339 
       
   340     def update_dimension_fields(self, instance, force=False, *args, **kwargs):
       
   341         """
       
   342         Updates field's width and height fields, if defined.
       
   343 
       
   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
       
   348         database.
       
   349 
       
   350         Dimensions can be forced to update with force=True, which is how
       
   351         ImageFileDescriptor.__set__ calls this method.
       
   352         """
       
   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:
       
   356             return
       
   357 
       
   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)
       
   362 
       
   363         # Nothing to update if we have no file and not being forced to update.
       
   364         if not file and not force:
       
   365             return
       
   366 
       
   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))
       
   370         )
       
   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:
       
   379             return
       
   380 
       
   381         # file should be an instance of ImageFieldFile or should be None.
       
   382         if file:
       
   383             width = file.width
       
   384             height = file.height
       
   385         else:
       
   386             # No file, so clear dimensions fields.
       
   387             width = None
       
   388             height = None
       
   389 
       
   390         # Update the width and height fields.
       
   391         if self.width_field:
       
   392             setattr(instance, self.width_field, width)
       
   393         if self.height_field:
       
   394             setattr(instance, self.height_field, height)
       
   395 
       
   396     def formfield(self, **kwargs):
       
   397         defaults = {'form_class': forms.ImageField}
       
   398         defaults.update(kwargs)
       
   399         return super(ImageField, self).formfield(**defaults)