web/lib/django/db/models/fields/files.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 import datetime
       
     2 import os
       
     3 
       
     4 import django.utils.copycompat as copy
       
     5 
       
     6 from django.conf import settings
       
     7 from django.db.models.fields import Field
       
     8 from django.core.files.base import File, ContentFile
       
     9 from django.core.files.storage import default_storage
       
    10 from django.core.files.images import ImageFile, get_image_dimensions
       
    11 from django.core.files.uploadedfile import UploadedFile
       
    12 from django.utils.functional import curry
       
    13 from django.db.models import signals
       
    14 from django.utils.encoding import force_unicode, smart_str
       
    15 from django.utils.translation import ugettext_lazy, ugettext as _
       
    16 from django import forms
       
    17 from django.db.models.loading import cache
       
    18 
       
    19 class FieldFile(File):
       
    20     def __init__(self, instance, field, name):
       
    21         super(FieldFile, self).__init__(None, name)
       
    22         self.instance = instance
       
    23         self.field = field
       
    24         self.storage = field.storage
       
    25         self._committed = True
       
    26 
       
    27     def __eq__(self, other):
       
    28         # Older code may be expecting FileField values to be simple strings.
       
    29         # By overriding the == operator, it can remain backwards compatibility.
       
    30         if hasattr(other, 'name'):
       
    31             return self.name == other.name
       
    32         return self.name == other
       
    33 
       
    34     def __ne__(self, other):
       
    35         return not self.__eq__(other)
       
    36 
       
    37     def __hash__(self):
       
    38         # Required because we defined a custom __eq__.
       
    39         return hash(self.name)
       
    40 
       
    41     # The standard File contains most of the necessary properties, but
       
    42     # FieldFiles can be instantiated without a name, so that needs to
       
    43     # be checked for here.
       
    44 
       
    45     def _require_file(self):
       
    46         if not self:
       
    47             raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
       
    48 
       
    49     def _get_file(self):
       
    50         self._require_file()
       
    51         if not hasattr(self, '_file') or self._file is None:
       
    52             self._file = self.storage.open(self.name, 'rb')
       
    53         return self._file
       
    54 
       
    55     def _set_file(self, file):
       
    56         self._file = file
       
    57 
       
    58     def _del_file(self):
       
    59         del self._file
       
    60 
       
    61     file = property(_get_file, _set_file, _del_file)
       
    62 
       
    63     def _get_path(self):
       
    64         self._require_file()
       
    65         return self.storage.path(self.name)
       
    66     path = property(_get_path)
       
    67 
       
    68     def _get_url(self):
       
    69         self._require_file()
       
    70         return self.storage.url(self.name)
       
    71     url = property(_get_url)
       
    72 
       
    73     def _get_size(self):
       
    74         self._require_file()
       
    75         if not self._committed:
       
    76             return len(self.file)
       
    77         return self.storage.size(self.name)
       
    78     size = property(_get_size)
       
    79 
       
    80     def open(self, mode='rb'):
       
    81         self._require_file()
       
    82         self.file.open(mode)
       
    83     # open() doesn't alter the file's contents, but it does reset the pointer
       
    84     open.alters_data = True
       
    85 
       
    86     # In addition to the standard File API, FieldFiles have extra methods
       
    87     # to further manipulate the underlying file, as well as update the
       
    88     # associated model instance.
       
    89 
       
    90     def save(self, name, content, save=True):
       
    91         name = self.field.generate_filename(self.instance, name)
       
    92         self.name = self.storage.save(name, content)
       
    93         setattr(self.instance, self.field.name, self.name)
       
    94 
       
    95         # Update the filesize cache
       
    96         self._size = len(content)
       
    97         self._committed = True
       
    98 
       
    99         # Save the object because it has changed, unless save is False
       
   100         if save:
       
   101             self.instance.save()
       
   102     save.alters_data = True
       
   103 
       
   104     def delete(self, save=True):
       
   105         # Only close the file if it's already open, which we know by the
       
   106         # presence of self._file
       
   107         if hasattr(self, '_file'):
       
   108             self.close()
       
   109             del self.file
       
   110 
       
   111         self.storage.delete(self.name)
       
   112 
       
   113         self.name = None
       
   114         setattr(self.instance, self.field.name, self.name)
       
   115 
       
   116         # Delete the filesize cache
       
   117         if hasattr(self, '_size'):
       
   118             del self._size
       
   119         self._committed = False
       
   120 
       
   121         if save:
       
   122             self.instance.save()
       
   123     delete.alters_data = True
       
   124 
       
   125     def _get_closed(self):
       
   126         file = getattr(self, '_file', None)
       
   127         return file is None or file.closed
       
   128     closed = property(_get_closed)
       
   129 
       
   130     def close(self):
       
   131         file = getattr(self, '_file', None)
       
   132         if file is not None:
       
   133             file.close()
       
   134 
       
   135     def __getstate__(self):
       
   136         # FieldFile needs access to its associated model field and an instance
       
   137         # it's attached to in order to work properly, but the only necessary
       
   138         # data to be pickled is the file's name itself. Everything else will
       
   139         # be restored later, by FileDescriptor below.
       
   140         return {'name': self.name, 'closed': False, '_committed': True, '_file': None}
       
   141 
       
   142 class FileDescriptor(object):
       
   143     """
       
   144     The descriptor for the file attribute on the model instance. Returns a
       
   145     FieldFile when accessed so you can do stuff like::
       
   146 
       
   147         >>> instance.file.size
       
   148 
       
   149     Assigns a file object on assignment so you can do::
       
   150 
       
   151         >>> instance.file = File(...)
       
   152 
       
   153     """
       
   154     def __init__(self, field):
       
   155         self.field = field
       
   156 
       
   157     def __get__(self, instance=None, owner=None):
       
   158         if instance is None:
       
   159             raise AttributeError(
       
   160                 "The '%s' attribute can only be accessed from %s instances."
       
   161                 % (self.field.name, owner.__name__))
       
   162 
       
   163         # This is slightly complicated, so worth an explanation.
       
   164         # instance.file`needs to ultimately return some instance of `File`,
       
   165         # probably a subclass. Additionally, this returned object needs to have
       
   166         # the FieldFile API so that users can easily do things like
       
   167         # instance.file.path and have that delegated to the file storage engine.
       
   168         # Easy enough if we're strict about assignment in __set__, but if you
       
   169         # peek below you can see that we're not. So depending on the current
       
   170         # value of the field we have to dynamically construct some sort of
       
   171         # "thing" to return.
       
   172 
       
   173         # The instance dict contains whatever was originally assigned
       
   174         # in __set__.
       
   175         file = instance.__dict__[self.field.name]
       
   176 
       
   177         # If this value is a string (instance.file = "path/to/file") or None
       
   178         # then we simply wrap it with the appropriate attribute class according
       
   179         # to the file field. [This is FieldFile for FileFields and
       
   180         # ImageFieldFile for ImageFields; it's also conceivable that user
       
   181         # subclasses might also want to subclass the attribute class]. This
       
   182         # object understands how to convert a path to a file, and also how to
       
   183         # handle None.
       
   184         if isinstance(file, basestring) or file is None:
       
   185             attr = self.field.attr_class(instance, self.field, file)
       
   186             instance.__dict__[self.field.name] = attr
       
   187 
       
   188         # Other types of files may be assigned as well, but they need to have
       
   189         # the FieldFile interface added to the. Thus, we wrap any other type of
       
   190         # File inside a FieldFile (well, the field's attr_class, which is
       
   191         # usually FieldFile).
       
   192         elif isinstance(file, File) and not isinstance(file, FieldFile):
       
   193             file_copy = self.field.attr_class(instance, self.field, file.name)
       
   194             file_copy.file = file
       
   195             file_copy._committed = False
       
   196             instance.__dict__[self.field.name] = file_copy
       
   197 
       
   198         # Finally, because of the (some would say boneheaded) way pickle works,
       
   199         # the underlying FieldFile might not actually itself have an associated
       
   200         # file. So we need to reset the details of the FieldFile in those cases.
       
   201         elif isinstance(file, FieldFile) and not hasattr(file, 'field'):
       
   202             file.instance = instance
       
   203             file.field = self.field
       
   204             file.storage = self.field.storage
       
   205 
       
   206         # That was fun, wasn't it?
       
   207         return instance.__dict__[self.field.name]
       
   208 
       
   209     def __set__(self, instance, value):
       
   210         instance.__dict__[self.field.name] = value
       
   211 
       
   212 class FileField(Field):
       
   213     # The class to wrap instance attributes in. Accessing the file object off
       
   214     # the instance will always return an instance of attr_class.
       
   215     attr_class = FieldFile
       
   216 
       
   217     # The descriptor to use for accessing the attribute off of the class.
       
   218     descriptor_class = FileDescriptor
       
   219 
       
   220     description = ugettext_lazy("File path")
       
   221 
       
   222     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
       
   223         for arg in ('primary_key', 'unique'):
       
   224             if arg in kwargs:
       
   225                 raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__))
       
   226 
       
   227         self.storage = storage or default_storage
       
   228         self.upload_to = upload_to
       
   229         if callable(upload_to):
       
   230             self.generate_filename = upload_to
       
   231 
       
   232         kwargs['max_length'] = kwargs.get('max_length', 100)
       
   233         super(FileField, self).__init__(verbose_name, name, **kwargs)
       
   234 
       
   235     def get_internal_type(self):
       
   236         return "FileField"
       
   237 
       
   238     def get_prep_lookup(self, lookup_type, value):
       
   239         if hasattr(value, 'name'):
       
   240             value = value.name
       
   241         return super(FileField, self).get_prep_lookup(lookup_type, value)
       
   242 
       
   243     def get_prep_value(self, value):
       
   244         "Returns field's value prepared for saving into a database."
       
   245         # Need to convert File objects provided via a form to unicode for database insertion
       
   246         if value is None:
       
   247             return None
       
   248         return unicode(value)
       
   249 
       
   250     def pre_save(self, model_instance, add):
       
   251         "Returns field's value just before saving."
       
   252         file = super(FileField, self).pre_save(model_instance, add)
       
   253         if file and not file._committed:
       
   254             # Commit the file to storage prior to saving the model
       
   255             file.save(file.name, file, save=False)
       
   256         return file
       
   257 
       
   258     def contribute_to_class(self, cls, name):
       
   259         super(FileField, self).contribute_to_class(cls, name)
       
   260         setattr(cls, self.name, self.descriptor_class(self))
       
   261         signals.post_delete.connect(self.delete_file, sender=cls)
       
   262 
       
   263     def delete_file(self, instance, sender, **kwargs):
       
   264         file = getattr(instance, self.attname)
       
   265         # If no other object of this type references the file,
       
   266         # and it's not the default value for future objects,
       
   267         # delete it from the backend.
       
   268         if file and file.name != self.default and \
       
   269             not sender._default_manager.filter(**{self.name: file.name}):
       
   270                 file.delete(save=False)
       
   271         elif file:
       
   272             # Otherwise, just close the file, so it doesn't tie up resources.
       
   273             file.close()
       
   274 
       
   275     def get_directory_name(self):
       
   276         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
       
   277 
       
   278     def get_filename(self, filename):
       
   279         return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))
       
   280 
       
   281     def generate_filename(self, instance, filename):
       
   282         return os.path.join(self.get_directory_name(), self.get_filename(filename))
       
   283 
       
   284     def save_form_data(self, instance, data):
       
   285         if data:
       
   286             setattr(instance, self.name, data)
       
   287 
       
   288     def formfield(self, **kwargs):
       
   289         defaults = {'form_class': forms.FileField, 'max_length': self.max_length}
       
   290         # If a file has been provided previously, then the form doesn't require
       
   291         # that a new file is provided this time.
       
   292         # The code to mark the form field as not required is used by
       
   293         # form_for_instance, but can probably be removed once form_for_instance
       
   294         # is gone. ModelForm uses a different method to check for an existing file.
       
   295         if 'initial' in kwargs:
       
   296             defaults['required'] = False
       
   297         defaults.update(kwargs)
       
   298         return super(FileField, self).formfield(**defaults)
       
   299 
       
   300 class ImageFileDescriptor(FileDescriptor):
       
   301     """
       
   302     Just like the FileDescriptor, but for ImageFields. The only difference is
       
   303     assigning the width/height to the width_field/height_field, if appropriate.
       
   304     """
       
   305     def __set__(self, instance, value):
       
   306         previous_file = instance.__dict__.get(self.field.name)
       
   307         super(ImageFileDescriptor, self).__set__(instance, value)
       
   308 
       
   309         # To prevent recalculating image dimensions when we are instantiating
       
   310         # an object from the database (bug #11084), only update dimensions if
       
   311         # the field had a value before this assignment.  Since the default
       
   312         # value for FileField subclasses is an instance of field.attr_class,
       
   313         # previous_file will only be None when we are called from
       
   314         # Model.__init__().  The ImageField.update_dimension_fields method
       
   315         # hooked up to the post_init signal handles the Model.__init__() cases.
       
   316         # Assignment happening outside of Model.__init__() will trigger the
       
   317         # update right here.
       
   318         if previous_file is not None:
       
   319             self.field.update_dimension_fields(instance, force=True)
       
   320 
       
   321 class ImageFieldFile(ImageFile, FieldFile):
       
   322     def delete(self, save=True):
       
   323         # Clear the image dimensions cache
       
   324         if hasattr(self, '_dimensions_cache'):
       
   325             del self._dimensions_cache
       
   326         super(ImageFieldFile, self).delete(save)
       
   327 
       
   328 class ImageField(FileField):
       
   329     attr_class = ImageFieldFile
       
   330     descriptor_class = ImageFileDescriptor
       
   331     description = ugettext_lazy("File path")
       
   332 
       
   333     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
       
   334         self.width_field, self.height_field = width_field, height_field
       
   335         FileField.__init__(self, verbose_name, name, **kwargs)
       
   336 
       
   337     def contribute_to_class(self, cls, name):
       
   338         super(ImageField, self).contribute_to_class(cls, name)
       
   339         # Attach update_dimension_fields so that dimension fields declared
       
   340         # after their corresponding image field don't stay cleared by
       
   341         # Model.__init__, see bug #11196.
       
   342         signals.post_init.connect(self.update_dimension_fields, sender=cls)
       
   343 
       
   344     def update_dimension_fields(self, instance, force=False, *args, **kwargs):
       
   345         """
       
   346         Updates field's width and height fields, if defined.
       
   347 
       
   348         This method is hooked up to model's post_init signal to update
       
   349         dimensions after instantiating a model instance.  However, dimensions
       
   350         won't be updated if the dimensions fields are already populated.  This
       
   351         avoids unnecessary recalculation when loading an object from the
       
   352         database.
       
   353 
       
   354         Dimensions can be forced to update with force=True, which is how
       
   355         ImageFileDescriptor.__set__ calls this method.
       
   356         """
       
   357         # Nothing to update if the field doesn't have have dimension fields.
       
   358         has_dimension_fields = self.width_field or self.height_field
       
   359         if not has_dimension_fields:
       
   360             return
       
   361 
       
   362         # getattr will call the ImageFileDescriptor's __get__ method, which
       
   363         # coerces the assigned value into an instance of self.attr_class
       
   364         # (ImageFieldFile in this case).
       
   365         file = getattr(instance, self.attname)
       
   366 
       
   367         # Nothing to update if we have no file and not being forced to update.
       
   368         if not file and not force:
       
   369             return
       
   370 
       
   371         dimension_fields_filled = not(
       
   372             (self.width_field and not getattr(instance, self.width_field))
       
   373             or (self.height_field and not getattr(instance, self.height_field))
       
   374         )
       
   375         # When both dimension fields have values, we are most likely loading
       
   376         # data from the database or updating an image field that already had
       
   377         # an image stored.  In the first case, we don't want to update the
       
   378         # dimension fields because we are already getting their values from the
       
   379         # database.  In the second case, we do want to update the dimensions
       
   380         # fields and will skip this return because force will be True since we
       
   381         # were called from ImageFileDescriptor.__set__.
       
   382         if dimension_fields_filled and not force:
       
   383             return
       
   384 
       
   385         # file should be an instance of ImageFieldFile or should be None.
       
   386         if file:
       
   387             width = file.width
       
   388             height = file.height
       
   389         else:
       
   390             # No file, so clear dimensions fields.
       
   391             width = None
       
   392             height = None
       
   393 
       
   394         # Update the width and height fields.
       
   395         if self.width_field:
       
   396             setattr(instance, self.width_field, width)
       
   397         if self.height_field:
       
   398             setattr(instance, self.height_field, height)
       
   399 
       
   400     def formfield(self, **kwargs):
       
   401         defaults = {'form_class': forms.ImageField}
       
   402         defaults.update(kwargs)
       
   403         return super(ImageField, self).formfield(**defaults)