Ticket #5361: filestorage.16.diff

File filestorage.16.diff, 69.9 KB (added by Marty Alchin, 17 years ago)

Some security enhancements, more tests, and an update to r7520

  • django/conf/global_settings.py

     
    216216# Path to the "jing" executable -- needed to validate XMLFields
    217217JING_PATH = "/usr/bin/jing"
    218218
     219# Default file storage mechanism that holds media.
     220DEFAULT_FILE_STORAGE = 'django.core.filestorage.filesystem.FileSystemStorage'
     221
    219222# Absolute path to the directory that holds media.
    220223# Example: "/home/media/media.lawrence.com/"
    221224MEDIA_ROOT = ''
  • django/core/filestorage/__init__.py

    Property changes on: django/core/filestorage
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
     
     1from django.conf import settings
     2from django.core.exceptions import ImproperlyConfigured
     3
     4def get_storage(import_path):
     5    try:
     6        dot = import_path.rindex('.')
     7    except ValueError:
     8        raise ImproperlyConfigured("%s isn't a storage module." % import_path)
     9    module, classname = import_path[:dot], import_path[dot+1:]
     10    try:
     11        mod = __import__(module, {}, {}, [''])
     12    except ImportError, e:
     13        raise ImproperlyConfigured('Error importing storage module %s: "%s"' % (module, e))
     14    try:
     15        storage_class = getattr(mod, classname)
     16    except AttributeError:
     17        raise ImproperlyConfigured('Storage module "%s" does not define a "%s" class.' % (module, classname))
     18    return storage_class()
     19
     20storage = get_storage(settings.DEFAULT_FILE_STORAGE)
  • django/core/filestorage/base.py

     
     1from StringIO import StringIO
     2
     3from django.utils.text import get_valid_filename
     4
     5class Storage(object):
     6    def get_valid_filename(self, filename):
     7        return get_valid_filename(filename)
     8
     9    def get_available_filename(self, filename):
     10        # If the filename already exists, keep adding an underscore to the name
     11        # of the file until the filename doesn't exist.
     12        while self.exists(filename):
     13            try:
     14                dot_index = filename.rindex('.')
     15            except ValueError: # filename has no dot
     16                filename += '_'
     17            else:
     18                filename = filename[:dot_index] + '_' + filename[dot_index:]
     19        return filename
     20
     21class RemoteFile(StringIO):
     22    """Sends files to remote storage automatically, when necessary."""
     23
     24    def __init__(self, data, mode, writer):
     25        self._mode = mode
     26        self._write_to_storage = writer
     27        self._is_dirty = False
     28        StringIO.__init__(self, data)
     29
     30    def write(self, data):
     31        if 'w' not in self._mode:
     32            raise AttributeError("File was opened for read-only access.")
     33        StringIO.write(self, data)
     34        self._is_dirty = True
     35
     36    def close(self):
     37        if self._is_dirty:
     38            self._write_to_storage(self.getvalue())
     39        StringIO.close(self)
  • django/core/filestorage/filesystem.py

     
     1import os
     2import urlparse
     3
     4from django.conf import settings
     5from django.core.exceptions import SuspiciousOperation
     6from django.utils.encoding import force_unicode, smart_str
     7from django.core.filestorage.base import Storage
     8from django.utils.text import force_unicode
     9from django.utils._os import safe_join
     10
     11class FileSystemStorage(Storage):
     12    """Standard filesystem storage"""
     13
     14    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
     15        self.location = os.path.abspath(location)
     16        self.base_url = base_url
     17
     18    def path(self, filename):
     19        try:
     20            path = safe_join(self.location, filename)
     21        except ValueError:
     22           raise SuspiciousOperation("Attempted access to '%s' denied." % filename)
     23        return os.path.normpath(path)
     24
     25    def filesize(self, filename):
     26        return os.path.getsize(self.path(filename))
     27
     28    def url(self, filename):
     29        return urlparse.urljoin(self.base_url, filename).replace('\\', '/')
     30
     31    def exists(self, filename):
     32        return os.path.exists(self.path(filename))
     33
     34    def open(self, filename, mode='rb'):
     35        return open(self.path(filename), mode)
     36
     37    def save(self, filename, raw_contents):
     38        directory = self.path(os.path.dirname(filename))
     39        if not os.path.exists(directory):
     40            os.makedirs(directory)
     41        elif not os.path.isdir(directory):
     42            raise IOError("%s exists and is not a directory." % directory)
     43
     44        filename = self.get_available_filename(filename)
     45
     46        # Write the file to disk.
     47        fp = self.open(filename, 'wb')
     48        fp.write(raw_contents)
     49        fp.close()
     50
     51        # Store filenames with forward slashes, even on Windows
     52        return force_unicode(filename.replace('\\', '/'))
     53
     54    def delete(self, filename):
     55        file_name = self.path(filename)
     56        # If the file exists, delete it from the filesystem.
     57        if os.path.exists(file_name):
     58            os.remove(file_name)
  • django/db/models/__init__.py

     
    88from django.db.models.base import Model, AdminOptions
    99from django.db.models.fields import *
    1010from django.db.models.fields.subclassing import SubfieldBase
     11from django.db.models.fields.files import FileField, ImageField
    1112from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED
    1213from django.db.models import signals
    1314from django.utils.functional import curry
  • django/db/models/base.py

     
    88import django.db.models.manager         # Ditto.
    99from django.core import validators
    1010from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
    11 from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
     11from django.db.models.fields import AutoField, FieldDoesNotExist
    1212from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
    1313from django.db.models.query import delete_objects, Q
    1414from django.db.models.options import Options, AdminOptions
     
    2020from django.utils.functional import curry
    2121from django.utils.encoding import smart_str, force_unicode, smart_unicode
    2222from django.conf import settings
     23from warnings import warn
    2324
    2425try:
    2526    set
     
    432433        return getattr(self, cachename)
    433434
    434435    def _get_FIELD_filename(self, field):
    435         if getattr(self, field.attname): # value is not blank
    436             return os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname))
    437         return ''
     436        warn("instance.get_%s_filename() is deprecated. Use instance.%s.path() instead." % \
     437            (field.attname, field.attname), DeprecationWarning)
     438        try:
     439            return getattr(self, field.attname).path()
     440        except ValueError:
     441            # For backward compatibility
     442            return settings.MEDIA_ROOT
    438443
    439444    def _get_FIELD_url(self, field):
    440         if getattr(self, field.attname): # value is not blank
    441             import urlparse
    442             return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
    443         return ''
     445        warn("instance.get_%s_url() is deprecated. Use instance.%s.url() instead." % \
     446            (field.attname, field.attname), DeprecationWarning)
     447        try:
     448            return getattr(self, field.attname).url()
     449        except ValueError:
     450            # For backward compatibility
     451            return settings.MEDIA_URL
    444452
    445453    def _get_FIELD_size(self, field):
    446         return os.path.getsize(self._get_FIELD_filename(field))
     454        warn("instance.get_%s_size() is deprecated. Use instance.%s.filesize() instead." % \
     455            (field.attname, field.attname), DeprecationWarning)
     456        return getattr(self, field.attname).filesize()
    447457
    448458    def _save_FIELD_file(self, field, filename, raw_contents, save=True):
    449         directory = field.get_directory_name()
    450         try: # Create the date-based directory if it doesn't exist.
    451             os.makedirs(os.path.join(settings.MEDIA_ROOT, directory))
    452         except OSError: # Directory probably already exists.
    453             pass
    454         filename = field.get_filename(filename)
     459        warn("instance.save_%s_file() is deprecated. Use instance.%s.save() instead." % \
     460            (field.attname, field.attname), DeprecationWarning)
     461        return getattr(self, field.attname).save(filename, raw_contents, save)
    455462
    456         # If the filename already exists, keep adding an underscore to the name of
    457         # the file until the filename doesn't exist.
    458         while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
    459             try:
    460                 dot_index = filename.rindex('.')
    461             except ValueError: # filename has no dot
    462                 filename += '_'
    463             else:
    464                 filename = filename[:dot_index] + '_' + filename[dot_index:]
    465 
    466         # Write the file to disk.
    467         setattr(self, field.attname, filename)
    468 
    469         full_filename = self._get_FIELD_filename(field)
    470         fp = open(full_filename, 'wb')
    471         fp.write(raw_contents)
    472         fp.close()
    473 
    474         # Save the width and/or height, if applicable.
    475         if isinstance(field, ImageField) and (field.width_field or field.height_field):
    476             from django.utils.images import get_image_dimensions
    477             width, height = get_image_dimensions(full_filename)
    478             if field.width_field:
    479                 setattr(self, field.width_field, width)
    480             if field.height_field:
    481                 setattr(self, field.height_field, height)
    482 
    483         # Save the object because it has changed unless save is False
    484         if save:
    485             self.save()
    486 
    487     _save_FIELD_file.alters_data = True
    488 
    489463    def _get_FIELD_width(self, field):
    490         return self._get_image_dimensions(field)[0]
     464        warn("instance.get_%s_width() is deprecated. Use instance.%s.width() instead." % \
     465            (field.attname, field.attname), DeprecationWarning)
     466        return getattr(self, field.attname).width()
    491467
    492468    def _get_FIELD_height(self, field):
    493         return self._get_image_dimensions(field)[1]
     469        warn("instance.get_%s_height() is deprecated. Use instance.%s.height() instead." % \
     470            (field.attname, field.attname), DeprecationWarning)
     471        return getattr(self, field.attname).height()
    494472
    495     def _get_image_dimensions(self, field):
    496         cachename = "__%s_dimensions_cache" % field.name
    497         if not hasattr(self, cachename):
    498             from django.utils.images import get_image_dimensions
    499             filename = self._get_FIELD_filename(field)
    500             setattr(self, cachename, get_image_dimensions(filename))
    501         return getattr(self, cachename)
    502 
    503473############################################
    504474# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
    505475############################################
  • django/db/models/fields/__init__.py

     
    11import copy
    22import datetime
    3 import os
    43import time
    54try:
    65    import decimal
     
    306305        name_prefix is a prefix to prepend to the "field_name" argument.
    307306        rel is a boolean specifying whether this field is in a related context.
    308307        """
     308        from django.db.models.fields import files
     309
    309310        field_objs, params = self.prepare_field_objs_and_params(manipulator, name_prefix)
    310311
    311312        # Add the "unique" validator(s).
     
    337338        # If this field is in a related context, check whether any other fields
    338339        # in the related object have core=True. If so, add a validator --
    339340        # RequiredIfOtherFieldsGiven -- to this FormField.
    340         if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
     341        if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, files.FileField):
    341342            # First, get the core fields, if any.
    342343            core_field_names = []
    343344            for f in opts.fields:
     
    749750        defaults.update(kwargs)
    750751        return super(EmailField, self).formfield(**defaults)
    751752
    752 class FileField(Field):
    753     def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
    754         self.upload_to = upload_to
    755         kwargs['max_length'] = kwargs.get('max_length', 100)
    756         Field.__init__(self, verbose_name, name, **kwargs)
    757 
    758     def get_internal_type(self):
    759         return "FileField"
    760 
    761     def get_db_prep_save(self, value):
    762         "Returns field's value prepared for saving into a database."
    763         # Need to convert UploadedFile objects provided via a form to unicode for database insertion
    764         if value is None:
    765             return None
    766         return unicode(value)
    767 
    768     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    769         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
    770         if not self.blank:
    771             if rel:
    772                 # This validator makes sure FileFields work in a related context.
    773                 class RequiredFileField(object):
    774                     def __init__(self, other_field_names, other_file_field_name):
    775                         self.other_field_names = other_field_names
    776                         self.other_file_field_name = other_file_field_name
    777                         self.always_test = True
    778                     def __call__(self, field_data, all_data):
    779                         if not all_data.get(self.other_file_field_name, False):
    780                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))
    781                             c(field_data, all_data)
    782                 # First, get the core fields, if any.
    783                 core_field_names = []
    784                 for f in opts.fields:
    785                     if f.core and f != self:
    786                         core_field_names.extend(f.get_manipulator_field_names(name_prefix))
    787                 # Now, if there are any, add the validator to this FormField.
    788                 if core_field_names:
    789                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
    790             else:
    791                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))
    792                 v.always_test = True
    793                 field_list[0].validator_list.append(v)
    794                 field_list[0].is_required = field_list[1].is_required = False
    795 
    796         # If the raw path is passed in, validate it's under the MEDIA_ROOT.
    797         def isWithinMediaRoot(field_data, all_data):
    798             f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
    799             if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
    800                 raise validators.ValidationError, _("Enter a valid filename.")
    801         field_list[1].validator_list.append(isWithinMediaRoot)
    802         return field_list
    803 
    804     def contribute_to_class(self, cls, name):
    805         super(FileField, self).contribute_to_class(cls, name)
    806         setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
    807         setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
    808         setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
    809         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save))
    810         dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
    811 
    812     def delete_file(self, instance):
    813         if getattr(instance, self.attname):
    814             file_name = getattr(instance, 'get_%s_filename' % self.name)()
    815             # If the file exists and no other object of this type references it,
    816             # delete it from the filesystem.
    817             if os.path.exists(file_name) and \
    818                 not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}):
    819                 os.remove(file_name)
    820 
    821     def get_manipulator_field_objs(self):
    822         return [oldforms.FileUploadField, oldforms.HiddenField]
    823 
    824     def get_manipulator_field_names(self, name_prefix):
    825         return [name_prefix + self.name + '_file', name_prefix + self.name]
    826 
    827     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    828         upload_field_name = self.get_manipulator_field_names('')[0]
    829         if new_data.get(upload_field_name, False):
    830             func = getattr(new_object, 'save_%s_file' % self.name)
    831             if rel:
    832                 func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)
    833             else:
    834                 func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)
    835 
    836     def get_directory_name(self):
    837         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
    838 
    839     def get_filename(self, filename):
    840         from django.utils.text import get_valid_filename
    841         f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
    842         return os.path.normpath(f)
    843 
    844     def save_form_data(self, instance, data):
    845         from django.newforms.fields import UploadedFile
    846         if data and isinstance(data, UploadedFile):
    847             getattr(instance, "save_%s_file" % self.name)(data.filename, data.content, save=False)
    848 
    849     def formfield(self, **kwargs):
    850         defaults = {'form_class': forms.FileField}
    851         # If a file has been provided previously, then the form doesn't require
    852         # that a new file is provided this time.
    853         # The code to mark the form field as not required is used by
    854         # form_for_instance, but can probably be removed once form_for_instance
    855         # is gone. ModelForm uses a different method to check for an existing file.
    856         if 'initial' in kwargs:
    857             defaults['required'] = False
    858         defaults.update(kwargs)
    859         return super(FileField, self).formfield(**defaults)
    860 
    861753class FilePathField(Field):
    862754    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
    863755        self.path, self.match, self.recursive = path, match, recursive
     
    894786        defaults.update(kwargs)
    895787        return super(FloatField, self).formfield(**defaults)
    896788
    897 class ImageField(FileField):
    898     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
    899         self.width_field, self.height_field = width_field, height_field
    900         FileField.__init__(self, verbose_name, name, **kwargs)
    901 
    902     def get_manipulator_field_objs(self):
    903         return [oldforms.ImageUploadField, oldforms.HiddenField]
    904 
    905     def contribute_to_class(self, cls, name):
    906         super(ImageField, self).contribute_to_class(cls, name)
    907         # Add get_BLAH_width and get_BLAH_height methods, but only if the
    908         # image field doesn't have width and height cache fields.
    909         if not self.width_field:
    910             setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))
    911         if not self.height_field:
    912             setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
    913 
    914     def get_internal_type(self):
    915         return "ImageField"
    916 
    917     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    918         FileField.save_file(self, new_data, new_object, original_object, change, rel, save)
    919         # If the image has height and/or width field(s) and they haven't
    920         # changed, set the width and/or height field(s) back to their original
    921         # values.
    922         if change and (self.width_field or self.height_field) and save:
    923             if self.width_field:
    924                 setattr(new_object, self.width_field, getattr(original_object, self.width_field))
    925             if self.height_field:
    926                 setattr(new_object, self.height_field, getattr(original_object, self.height_field))
    927             new_object.save()
    928 
    929     def formfield(self, **kwargs):
    930         defaults = {'form_class': forms.ImageField}
    931         defaults.update(kwargs)
    932         return super(ImageField, self).formfield(**defaults)
    933 
    934789class IntegerField(Field):
    935790    empty_strings_allowed = False
    936791    def get_manipulator_field_objs(self):
  • django/db/models/fields/files.py

     
     1import datetime
     2import os
     3
     4from django.conf import settings
     5from django.db.models.fields import Field
     6from django.core.filestorage import storage as default_storage
     7from django.utils.functional import curry
     8from django.dispatch import dispatcher
     9from django.db.models import signals
     10from django.utils.encoding import force_unicode, smart_str
     11from django.utils.translation import ugettext_lazy, ugettext as _
     12from django import oldforms
     13from django import newforms as forms
     14from django.core import validators
     15
     16class File(object):
     17    def __init__(self, instance, field, filename):
     18        self.instance = instance
     19        self.field = field
     20        self.storage = field.storage
     21        self.filename = filename or u''
     22
     23    def __unicode__(self):
     24        return self.filename or u''
     25
     26    def __repr__(self):
     27        return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self) or u'None'))
     28
     29    def __nonzero__(self):
     30        return not not self.filename
     31
     32    def __eq__(self, other):
     33        return self.filename == other
     34
     35    def path(self):
     36        if not self:
     37            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
     38        return self.storage.path(self.filename)
     39
     40    def url(self):
     41        if not self:
     42            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
     43        return self.storage.url(self.filename)
     44
     45    def filesize(self):
     46        if not self:
     47            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
     48        if not hasattr(self, '_filesize'):
     49            self._filesize = self.storage.filesize(self.filename)
     50        return self._filesize
     51
     52    def open(self, mode='rb'):
     53        if not self:
     54            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
     55        return self.storage.open(self.filename, mode)
     56
     57    def save(self, filename, raw_contents, save=True):
     58        filename = self.field.generate_filename(self.instance, filename)
     59        self.filename = self.storage.save(filename, raw_contents)
     60        setattr(self.instance, self.field.name, self.filename)
     61        self._has_file = True
     62
     63        # Update the filesize cache
     64        self._filesize = len(raw_contents)
     65
     66        # Save the object because it has changed, unless save is False
     67        if save:
     68            self.instance.save()
     69
     70    def delete(self, save=True):
     71        if not self:
     72            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
     73        self.storage.delete(self.filename)
     74
     75        self.filename = None
     76        setattr(self.instance, self.field.name, self.filename)
     77
     78        # Delete the filesize cache
     79        if hasattr(self, '_filesize'):
     80            del self._filesize
     81
     82        if save:
     83            self.instance.save()
     84
     85class FileDescriptor(object):
     86    def __init__(self, field):
     87        self.field = field
     88
     89    def __get__(self, instance=None, owner=None):
     90        if instance is None:
     91            raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
     92        return self.field.attr_class(instance, self.field, instance.__dict__[self.field.name])
     93
     94    def __set__(self, instance, value):
     95        instance.__dict__[self.field.name] = value
     96
     97class FileField(Field):
     98    attr_class = File
     99
     100    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
     101        for arg in ('core', 'primary_key', 'unique'):
     102            if arg in kwargs:
     103                raise TypeError("__init__() got an unexpected keyword argument '%s'" % arg)
     104
     105        self.storage = storage or default_storage
     106        self.upload_to = upload_to
     107        if callable(upload_to):
     108            self.generate_filename = upload_to
     109
     110        kwargs['max_length'] = kwargs.get('max_length', 100)
     111        super(FileField, self).__init__(verbose_name, name, **kwargs)
     112
     113    def get_internal_type(self):
     114        return "FileField"
     115
     116    def get_db_prep_lookup(self, lookup_type, value):
     117        if hasattr(value, 'filename'):
     118            value = value.filename
     119        return super(FileField, self).get_db_prep_lookup(lookup_type, value)
     120
     121    def get_db_prep_save(self, value):
     122        "Returns field's value prepared for saving into a database."
     123        # Need to convert UploadedFile objects provided via a form to unicode for database insertion
     124        if value is None:
     125            return None
     126        return unicode(value.filename)
     127
     128    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
     129        field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
     130        if not self.blank:
     131            if rel:
     132                # This validator makes sure FileFields work in a related context.
     133                class RequiredFileField(object):
     134                    def __init__(self, other_field_names, other_file_field_name):
     135                        self.other_field_names = other_field_names
     136                        self.other_file_field_name = other_file_field_name
     137                        self.always_test = True
     138                    def __call__(self, field_data, all_data):
     139                        if not all_data.get(self.other_file_field_name, False):
     140                            c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))
     141                            c(field_data, all_data)
     142                # First, get the core fields, if any.
     143                core_field_names = []
     144                for f in opts.fields:
     145                    if f.core and f != self:
     146                        core_field_names.extend(f.get_manipulator_field_names(name_prefix))
     147                # Now, if there are any, add the validator to this FormField.
     148                if core_field_names:
     149                    field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
     150            else:
     151                v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))
     152                v.always_test = True
     153                field_list[0].validator_list.append(v)
     154                field_list[0].is_required = field_list[1].is_required = False
     155
     156        # If the raw path is passed in, validate it's under the MEDIA_ROOT.
     157        def isWithinMediaRoot(field_data, all_data):
     158            f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
     159            if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
     160                raise validators.ValidationError(_("Enter a valid filename."))
     161        field_list[1].validator_list.append(isWithinMediaRoot)
     162        return field_list
     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        setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
     168        setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
     169        setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
     170        setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save))
     171        dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
     172
     173    def delete_file(self, instance, sender):
     174        filename = getattr(instance, self.attname).filename
     175        # If no other object of this type references the file,
     176        # and it's not the default value for future objects,
     177        # delete it from the backend.
     178        if filename and filename != self.default and \
     179            not sender._default_manager.filter(**{self.name: filename}):
     180                self.storage.delete(filename)
     181
     182    def get_manipulator_field_objs(self):
     183        return [oldforms.FileUploadField, oldforms.HiddenField]
     184
     185    def get_manipulator_field_names(self, name_prefix):
     186        return [name_prefix + self.name + '_file', name_prefix + self.name]
     187
     188    def save_file(self, new_data, new_object, original_object, change, rel, save=True):
     189        upload_field_name = self.get_manipulator_field_names('')[0]
     190        if new_data.get(upload_field_name, False):
     191            if rel:
     192                field = new_data[upload_field_name][0]
     193            else:
     194                field = new_data[upload_field_name]
     195            filename = self.get_filename(field["filename"])
     196            getattr(new_object, self.attname).save(filename, field["content"], save)
     197
     198    def get_directory_name(self):
     199        return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
     200
     201    def get_filename(self, filename):
     202        return os.path.normpath(self.storage.get_valid_filename(os.path.basename(filename)))
     203
     204    def generate_filename(self, instance, filename):
     205        return os.path.join(self.get_directory_name(), self.get_filename(filename))
     206
     207    def save_form_data(self, instance, data):
     208        from django.newforms.fields import UploadedFile
     209        if data and isinstance(data, UploadedFile):
     210            getattr(instance, self.attname).save(data.filename, data.content, save=False)
     211
     212    def formfield(self, **kwargs):
     213        defaults = {'form_class': forms.FileField}
     214        # If a file has been provided previously, then the form doesn't require
     215        # that a new file is provided this time.
     216        # The code to mark the form field as not required is used by
     217        # form_for_instance, but can probably be removed once form_for_instance
     218        # is gone. ModelForm uses a different method to check for an existing file.
     219        if 'initial' in kwargs:
     220            defaults['required'] = False
     221        defaults.update(kwargs)
     222        return super(FileField, self).formfield(**defaults)
     223
     224class ImageFile(File):
     225    def get_width(self):
     226        return self._get_image_dimensions()[0]
     227
     228    def get_height(self):
     229        return self._get_image_dimensions()[1]
     230
     231    def _get_image_dimensions(self):
     232        if not hasattr(self, '_dimensions_cache'):
     233            from django.utils.images import get_image_dimensions
     234            self._dimensions_cache = get_image_dimensions(self.open())
     235        return self._dimensions_cache
     236
     237    def save(self, filename, raw_contents, save=True):
     238        super(ImageFile, self).save(filename, raw_contents, save)
     239       
     240        # Update the cache for image dimensions
     241        from django.utils.images import get_image_dimensions
     242        from cStringIO import StringIO
     243        self._dimensions_cache = get_image_dimensions(StringIO(raw_contents))
     244
     245    def delete(self, save=True):
     246        # Clear the image dimensions cache
     247        del self._dimensions_cache
     248
     249        super(ImageFile, self).delete(save)
     250
     251class ImageField(FileField):
     252    attr_class = ImageFile
     253
     254    def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
     255        self.width_field, self.height_field = width_field, height_field
     256        FileField.__init__(self, verbose_name, name, **kwargs)
     257
     258    def get_manipulator_field_objs(self):
     259        return [oldforms.ImageUploadField, oldforms.HiddenField]
     260
     261    def contribute_to_class(self, cls, name):
     262        super(ImageField, self).contribute_to_class(cls, name)
     263        # Add get_BLAH_width and get_BLAH_height methods, but only if the
     264        # image field doesn't have width and height cache fields.
     265        if not self.width_field:
     266            setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))
     267        if not self.height_field:
     268            setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
     269
     270    def get_internal_type(self):
     271        return "ImageField"
     272
     273    def save_file(self, new_data, new_object, original_object, change, rel, save=True):
     274        # If the image has height and/or width field(s) and they haven't
     275        # changed, set the width and/or height field(s) back to their original
     276        # values.
     277        if self.width_field or self.height_field:
     278            if original_object and not change:
     279                if self.width_field:
     280                    setattr(new_object, self.width_field, getattr(original_object, self.width_field))
     281                if self.height_field:
     282                    setattr(new_object, self.height_field, getattr(original_object, self.height_field))
     283            else:
     284                from cStringIO import StringIO
     285                from django.utils.images import get_image_dimensions
     286
     287                upload_field_name = self.get_manipulator_field_names('')[0]
     288                if rel:
     289                    field = new_data[upload_field_name][0]
     290                else:
     291                    field = new_data[upload_field_name]
     292
     293                # Get the width and height from the raw content to avoid extra
     294                # unnecessary trips to the file backend.
     295                width, height = get_image_dimensions(StringIO(field["content"]))
     296
     297                if self.width_field:
     298                    setattr(new_object, self.width_field, width)
     299                if self.height_field:
     300                    setattr(new_object, self.height_field, height)
     301        FileField.save_file(self, new_data, new_object, original_object, change, rel, save)
     302
     303    def formfield(self, **kwargs):
     304        defaults = {'form_class': forms.ImageField}
     305        defaults.update(kwargs)
     306        return super(ImageField, self).formfield(**defaults)
  • django/db/models/manipulators.py

     
    11from django.core.exceptions import ObjectDoesNotExist
    22from django import oldforms
    33from django.core import validators
    4 from django.db.models.fields import FileField, AutoField
     4from django.db.models.fields import AutoField
     5from django.db.models.fields.files import FileField
    56from django.dispatch import dispatcher
    67from django.db.models import signals
    78from django.utils.functional import curry
  • django/utils/images.py

     
    66
    77import ImageFile
    88
    9 def get_image_dimensions(path):
    10     """Returns the (width, height) of an image at a given path."""
     9def get_image_dimensions(file_or_path):
     10    """Returns the (width, height) of an image, given an open file or a path."""
    1111    p = ImageFile.Parser()
    12     fp = open(path, 'rb')
     12    if hasattr(file_or_path, 'read'):
     13        fp = file_or_path
     14    else:
     15        fp = open(file_or_path, 'rb')
    1316    while 1:
    1417        data = fp.read(1024)
    1518        if not data:
  • docs/custom_model_fields.txt

     
    580580       instance, not a ``HandField``). So if your ``__unicode__()`` method
    581581       automatically converts to the string form of your Python object, you can
    582582       save yourself a lot of work.
     583
     584Writing a ``FileField`` subclass
     585=================================
     586
     587In addition to the above methods, fields that deal with files have a few other
     588special requirements which must be taken into account. The majority of the
     589mechanics provided by ``FileField``, such as controlling database storage and
     590retrieval, can remain unchanged, leaving subclasses to deal with the challenge
     591of supporting a particular type of file.
     592
     593Django provides a ``File`` class, which is used as a proxy to the file's
     594contents and operations. This can be subclassed to customzie hwo the file is
     595accessed, and what methods are available. It lives at
     596``django.db.models.fields.files``, and its default behavior is explained in the
     597`file documentation`_.
     598
     599Once a subclass of ``File`` is created, the new ``FileField`` subclass must be
     600told to use it. To do so, simply assign the new ``File`` subclass to the special
     601``attr_class`` attribute of the ``FileField`` subclass.
     602
     603.. _file documentation: ../files/
     604
     605A few suggestions
     606------------------
     607
     608In addition to the above details, there are a few guidelines which can greatly
     609improve the efficiency and readability of the field's code.
     610
     611    1. The source for Django's own ``ImageField`` (in
     612       ``django/db/models/fields/files.py``) is a great example of how to
     613       subclass ``FileField`` to support a particular type of file, as it
     614       incorporates all of the techniques described above.
     615
     616    2. Cache file attributes wherever possible. Since files may be stored in
     617       remote storage systems, retrieving them may cost extra time, or even
     618       money, that isn't always necessary. Once a file is retrieved to obtain
     619       some data about its content, cache as much of that data as possible to
     620       reduce the number of times the file must be retrieved on subsequent
     621       calls for that information.
  • docs/db-api.txt

     
    21982198get_FOO_filename()
    21992199------------------
    22002200
     2201**Deprecated in Django development version. See `managing files`_ for the new,
     2202preferred method for dealing with files.**
     2203
    22012204For every ``FileField``, the object will have a ``get_FOO_filename()`` method,
    22022205where ``FOO`` is the name of the field. This returns the full filesystem path
    22032206to the file, according to your ``MEDIA_ROOT`` setting.
     
    22082211get_FOO_url()
    22092212-------------
    22102213
     2214**Deprecated in Django development version. See `managing files`_ for the new,
     2215preferred method for dealing with files.**
     2216
    22112217For every ``FileField``, the object will have a ``get_FOO_url()`` method,
    22122218where ``FOO`` is the name of the field. This returns the full URL to the file,
    22132219according to your ``MEDIA_URL`` setting. If the value is blank, this method
     
    22162222get_FOO_size()
    22172223--------------
    22182224
     2225**Deprecated in Django development version. See `managing files`_ for the new,
     2226preferred method for dealing with files.**
     2227
    22192228For every ``FileField``, the object will have a ``get_FOO_size()`` method,
    22202229where ``FOO`` is the name of the field. This returns the size of the file, in
    22212230bytes. (Behind the scenes, it uses ``os.path.getsize``.)
     
    22232232save_FOO_file(filename, raw_contents)
    22242233-------------------------------------
    22252234
     2235**Deprecated in Django development version. See `managing files`_ for the new,
     2236preferred method for dealing with files.**
     2237
    22262238For every ``FileField``, the object will have a ``save_FOO_file()`` method,
    22272239where ``FOO`` is the name of the field. This saves the given file to the
    22282240filesystem, using the given filename. If a file with the given filename already
     
    22322244get_FOO_height() and get_FOO_width()
    22332245------------------------------------
    22342246
     2247**Deprecated in Django development version. See `managing files`_ for the new,
     2248preferred method for dealing with files.**
     2249
    22352250For every ``ImageField``, the object will have ``get_FOO_height()`` and
    22362251``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
    22372252returns the height (or width) of the image, as an integer, in pixels.
    22382253
     2254.. _`managing files`: ../files/
     2255
    22392256Shortcuts
    22402257=========
    22412258
  • docs/files.txt

     
     1==============
     2Managing files
     3==============
     4
     5**New in Django development version**
     6
     7When dealing with files, Django provides a number of features to make this task
     8easier and more portable. A storage protocol is available to allow files to be
     9stored in a variety of locations, and a special object is provided to allow
     10models to make use of this protocol, without having to worry about which storage
     11system is being used.
     12
     13Using files in models
     14=====================
     15
     16When accessing a ``FileField`` attached to a model, a special object provides
     17access to the file and information about it.
     18
     19Example
     20-------
     21
     22Consider the following model, using an ``ImageField`` to store a product photo::
     23
     24    class Product(models.Model):
     25        name = models.CharField(maxlength=255)
     26        price = models.DecimalField(max_digits=5, decimal_places=2)
     27        photo = models.ImageField(upload_to='product_photos')
     28
     29Your views can then use the ``photo`` attribute with the functions described
     30above, as follows::
     31
     32    >>> car = Product.object.get(name="'57 Chevy")
     33    >>> car.photo
     34    <ImageFile: 123.jpg>
     35    >>> car.photo.url()
     36    '/products/photo/123.jpg'
     37    >>> car.photo.width(), car.photo.height()
     38    (800, 600)
     39
     40``path()``
     41----------
     42
     43Returns the absolute path to the file's location on a local filesystem. For
     44storage systems which do not store files locally, this will return `None`.
     45
     46``url()``
     47---------
     48
     49Provides a URL where the content of the file can be retrieved. Therefore,
     50returned from this method is suitable for use as the destination of a link to
     51the file.
     52
     53``filesize()``
     54--------------
     55
     56Returns the size of the file, as an integer.
     57
     58``open(mode='rb')``
     59-------------------
     60
     61Returns an open file object, providing read or write access to the file's
     62contents. The ``mode`` argument allows the same values as Python's standard
     63``open()`` function.
     64
     65``save(filename, raw_contents, save=True)``
     66-------------------------------------------
     67
     68Saves a new file with the filename and contents provided. This will not replace
     69the existing file, but will create a new file and update the object to point to
     70it. The optional ``save`` argument dictates whether the model instance will be
     71saved to the database immediately.
     72
     73``width() and height()``
     74------------------------
     75
     76When using an ``ImageField``, these two methods will be available, providing
     77easy access to the dimensions of the image.
     78
     79Using a storage system with FileField
     80=====================================
     81
     82When using a storage system, supply whatever options are appropriate for
     83that system when creating a new object. Then pass that object as the ``storage``
     84argument to a ``FileField``. Details on the requirements for the included
     85storage system can be found below.
     86
     87If using the default storage system, it is not necessary to create a storage
     88object explicitly. In this case, the ``FileField`` will use the one referenced
     89by the `DEFAULT_FILE_STORAGE setting`_.
     90
     91See the ```FileField`` documentation`_ for more information on using the field.
     92
     93.. _DEFAULT_FILE_STORAGE setting: ../settings/#default-file-storage
     94.. _FileField documentation: ../model-api/#filefield
     95
     96For example, the following code will explicitly use the ``FileSystemStorage``::
     97
     98    from django.db import models
     99    from django.core.filestorage.filesystem import FileSystemStorage
     100   
     101    fs = FileSystemStorage(location='product_photos')
     102   
     103    class Product(models.Model):
     104        name = models.CharField(maxlength=255)
     105        price = models.DecimalField(max_digits=5, decimal_places=2)
     106        photo = models.ImageField(storage=fs)
     107
     108Using a storage system on its own
     109=================================
     110
     111Storage systems may also be used directly, without being attached to a model.
     112Simply use the following API on any instantiated storage system to access files
     113without having to worry about the underlying mechanism.
     114
     115In addition to explicit storage mechanisms, the file storage module,
     116``django.core.filestorage``, exports a ``storage`` object that's automatically
     117created from the ``DEFAULT_FILE_STORAGE`` setting::
     118
     119    >>> from django.core.filestorage import storage
     120
     121With a functional storage system on hand, managing files is quite simple, with a
     122few basic methods to handle the most common operations::
     123
     124    >>> path = storage.save('/path/to/file', 'new content')
     125    >>> path
     126    u'/path/to/file'
     127    >>> storage.filesize(path)
     128    11
     129    >>> storage.open(path).read()
     130    'new content'
     131    >>> storage.delete(path)
     132    >>> storage.exists(path)
     133    False
     134
     135``exists(filename)``
     136--------------------
     137
     138Returns ``True`` or ``False, indicating whether there is already a file present
     139at the location referenced by``filename``.
     140
     141``open(filename, mode='rb')``
     142-----------------------------
     143
     144Returns an open file, or file-like, object to provide access to the contents of
     145the file referenced by ``filename``. The ``mode`` argument allows the same
     146values as Python's standard ``open()`` function.
     147
     148``filesize(filename)``
     149----------------------
     150
     151Returns the total size of the file referenced by ``filename``, as an integer.
     152
     153``url(filename)``
     154-----------------
     155
     156Returns the URL where the contents of the file referenced by ``filename`` can
     157be accessed.
     158
     159``save(filename, raw_contents)``
     160--------------------------------
     161
     162Saves a new file using the storage system, preferably with the name specified.
     163If there already exists a file at the location referenced by ``filename``, this
     164may modify the filename as necessary to locate one that is available. Once the
     165file is saved, this method will return the filename where the file was actually
     166stored.
     167
     168``delete(filename)``
     169--------------------
     170
     171Deletes the file referenced by ``filename``. If the file does not already exist,
     172this method will simply return without raising an exception.
     173
     174Available storage systems
     175=========================
     176
     177Only one storage system is supplied in the official Django distribution, but
     178more may be available elsewhere. If you'd like to use a different storage system
     179than the one listed below, see the documentation included with it.
     180
     181``django.core.filestorage.filesystem.FileSystemStorage``
     182--------------------------------------------------------
     183
     184This simply stores files on the system's standard filesystem.
     185
     186    ======================  ===================================================
     187    Argument                Description
     188    ======================  ===================================================
     189    ``location``            Optional. Absolute path to the directory that will
     190                            hold the files. If omitted, it will be set to the
     191                            value of your ``MEDIA_ROOT`` setting.
     192    ``base_url``            Optional. URL that serves the files stored at this
     193                            location. If omitted, it will default to the value
     194                            of your ``MEDIA_URL`` setting.
     195    ======================  ===================================================
     196
     197Writing a storage system
     198========================
     199
     200While the default filesystem storage is suitable for most needs, there are many
     201other storage mechanisms that may be used, and situations that will require
     202special processing. In order to use Django in these environments, it's fairly
     203simple to write a new storage system, creating a wrapper around whatever
     204libraries are used to access your files, or simply customizing method calls on
     205an existing storage class.
     206
     207If a storage system requires any configuration options to determine how it
     208should access the underlying storage mechanism or cusotmize its behavior in
     209other ways, those options should be specified in a particular way. Because the
     210default storage system is specified as a string, Django must be able to
     211instantiate it without any arguments, and any required arguments should be
     212specified as global settings, which can be referenced from the storage system.
     213For example::
     214
     215    from django.conf import settings
     216    from django.core.filestorage.base import Storage
     217
     218    class CustomStorage(Storage):
     219        def __init__(self, option=settings.CUSTOM_STORAGE_OPTION):
     220            ...
     221
     222All storage systems must implement the methods described above, but Django also
     223uses two other methods to assist in the process. When writing a custom class,
     224these methods may be inherited from the built-in ``Storage`` class, living at
     225``django.core.filestorage.base``. When extending an existing storage class, they
     226can be overriden to allow a great deal of customization.
     227
     228``get_valid_filename(filename)``
     229--------------------------------
     230
     231Returns a filename suitable for use with the underlying storage system. The
     232``filename`` argument passed to this method is the original filename sent to the
     233server, after having any path information removed. Override this to customize
     234how non-standard characters are converted to safe filenames.
     235
     236The code provided on ``Storage`` retains only alpha-numeric characters, periods
     237and underscores from the original filename, removing everything else.
     238
     239``get_available_filename(filename)``
     240------------------------------------
     241
     242Returns a filename that is available in the storage mechanism, possibly taking
     243the provided filename into account. The ``filename`` argument passed to this
     244method will have already cleaned to a filename valid for the storage system,
     245according to the ``get_valid_filename()`` method described above.
     246
     247The code provided on ``Storage`` simply appends underscores to the filename
     248until it finds one that's available in the destination directory.
     249
     250Opening remote files
     251--------------------
     252
     253When accessing a file stored at a remote location, the object returned by
     254``open()`` should function like a standard `file object`_, but to keep
     255network traffic to a minimum, writes to the remote storage system should only
     256occur if actually necessary. To make this task easier, Django provides a class
     257to automate this process.
     258
     259Living at ``django.core.filestorage.base``, the ``RemoteFile`` class simulates
     260a standard Python `file object`_, but can write changes to a remote storage
     261system when application using a function provided by the storage system.
     262Creating an instance of this object requires three arguments, which are
     263described below.
     264
     265    ======================  ===================================================
     266    Argument                Description
     267    ======================  ===================================================
     268    ``data``                The raw content of the file.
     269    ``mode``                The access mode that was passed to the ``open()``
     270                            method.
     271    ``writer``              A function that will be used to write the contents
     272                            to the underlying storage mechanism. The function
     273                            provided here will need to take a single argument,
     274                            which will be the raw content to be written to the
     275                            file.
     276    ======================  ===================================================
     277
     278.. _file object: http://docs.python.org/lib/bltin-file-objects.html
     279
  • docs/model-api.txt

     
    230230``FileField``
    231231~~~~~~~~~~~~~
    232232
    233 A file-upload field. Has one **required** argument:
     233A file-upload field. Has two special arguments, of which the first is
     234**required**:
    234235
    235236    ======================  ===================================================
    236237    Argument                Description
    237238    ======================  ===================================================
    238     ``upload_to``           A local filesystem path that will be appended to
    239                             your ``MEDIA_ROOT`` setting to determine the
    240                             output of the ``get_<fieldname>_url()`` helper
    241                             function.
     239    ``upload_to``           Required. A filesystem-style path that will be
     240                            prepended to the filename before being committed to
     241                            the final storage destination.
     242
     243                            **New in Django development version**
     244
     245                            This may also be a callable, such as a function,
     246                            which will be called to obtain the upload path,
     247                            including the filename. See below for details.
     248
     249    ``storage``             **New in Django development version**
     250
     251                            Optional. A storage object, which handles the
     252                            storage and retrieval of your files. See `managing
     253                            files`_ for details on how to provide this object.
    242254    ======================  ===================================================
    243255
    244 This path may contain `strftime formatting`_, which will be replaced by the
    245 date/time of the file upload (so that uploaded files don't fill up the given
    246 directory).
     256.. _managing files: ../files/
    247257
     258The ``upload_to`` path may contain `strftime formatting`_, which will be
     259replaced by the date/time of the file upload (so that uploaded files don't fill
     260up the given directory).
     261
     262**New in Django development version**
     263
     264If a callable is provided for the ``upload_to`` argument, that callable must be
     265able to accept two arguments, and return a Unix-style path (with forward
     266slashes) to be passed along to the storage system. The two arguments that will
     267be passed are:
     268
     269    ======================  ===================================================
     270    Argument                Description
     271    ======================  ===================================================
     272    ``instance``            An instance of the model where the ``FileField`` is
     273                            defined. More specifically, this is the particular
     274                            instance where the current file is being attached.
     275                           
     276                            **Note**: In most cases, this object will not have
     277                            been saved to the database yet, so if it uses the
     278                            default ``AutoField``, *it might not yet have a
     279                            value for its primary key field*.
     280
     281    ``filename``            The filename that was originally given to the file.
     282                            This may or may not be taken into account when
     283                            determining the final destination path.
     284    ======================  ===================================================
     285
    248286The admin represents this field as an ``<input type="file">`` (a file-upload
    249287widget).
    250288
    251 Using a ``FileField`` or an ``ImageField`` (see below) in a model takes a few
    252 steps:
     289Using a ``FileField`` or an ``ImageField`` (see below) in a model without a
     290specified storage system takes a few steps:
    253291
    254292    1. In your settings file, you'll need to define ``MEDIA_ROOT`` as the
    255293       full path to a directory where you'd like Django to store uploaded
  • docs/settings.txt

     
    409409isn't manually specified. Used with ``DEFAULT_CHARSET`` to construct the
    410410``Content-Type`` header.
    411411
     412DEFAULT_FILE_STORAGE
     413--------------------
     414
     415Default: ``'django.core.filestorage.filesystem.FileSystemStorage'``
     416
     417Default file storage class to be used for any file-related operations that don't
     418specify a particular storage system. See the `file documentation`_ for details.
     419
     420.. _file documentation: ../files/
     421
    412422DEFAULT_FROM_EMAIL
    413423------------------
    414424
  • tests/modeltests/files/__init__.py

    Property changes on: tests/modeltests/files
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
     
     1
  • tests/modeltests/files/models.py

     
     1"""
     242. Storing files according to a custom storage system
     3
     4FileField and its variations can take a "storage" argument to specify how and
     5where files should be stored.
     6"""
     7
     8import tempfile
     9
     10from django.db import models
     11from django.core.filestorage.filesystem import FileSystemStorage
     12from django.core.cache import cache
     13
     14temp_dir = tempfile.gettempdir()
     15
     16temp_storage = FileSystemStorage(location=temp_dir)
     17
     18# Write out a file to be used as default content
     19temp_storage.save('tests/default.txt', 'default_content')
     20open('%s/tests/default.txt' % temp_dir, 'w').write('default content')
     21
     22class Storage(models.Model):
     23    def custom_upload_to(self, filename):
     24        return 'foo'
     25
     26    normal = models.FileField(storage=temp_storage, upload_to='tests')
     27    custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to)
     28    default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt')
     29
     30__test__ = {'API_TESTS':"""
     31# An object without a file has limited functionality
     32
     33>>> obj1 = Storage()
     34>>> obj1.normal
     35<File: None>
     36>>> obj1.normal.filesize()
     37Traceback (most recent call last):
     38...
     39ValueError: The 'normal' attribute has no file associated with it.
     40
     41# Saving a file enables full functionality
     42
     43>>> obj1.normal.save('django_test.txt', 'content')
     44>>> obj1.normal
     45<File: tests/django_test.txt>
     46>>> obj1.normal.filesize()
     477
     48>>> obj1.normal.open().read()
     49'content'
     50
     51# Save another file with the same name
     52
     53>>> obj2 = Storage()
     54>>> obj2.normal.save('django_test.txt', 'more content')
     55>>> obj2.normal
     56<File: tests/django_test_.txt>
     57>>> obj2.normal.filesize()
     5812
     59
     60# Push the objects into the cache to make sure they pickle properly
     61
     62>>> cache.set('obj1', obj1)
     63>>> cache.set('obj2', obj2)
     64>>> cache.get('obj2').normal
     65<File: tests/django_test_.txt>
     66
     67# Deleting an object deletes the file it uses, if there are no other objects
     68# still using that file
     69
     70>>> obj2.delete()
     71>>> obj2.normal.save('django_test.txt', 'more content')
     72>>> obj2.normal
     73<File: tests/django_test_.txt>
     74
     75# Default values allow an object to access a single file
     76
     77>>> obj3 = Storage.objects.create()
     78>>> obj3.default
     79<File: tests/default.txt>
     80>>> obj3.default.open().read()
     81'default content'
     82
     83# But it shouldn't be deleted, even if there are no more objects using it
     84
     85>>> obj3.delete()
     86>>> obj3 = Storage()
     87>>> obj3.default.open().read()
     88'default content'
     89
     90# Clean up the temporary files
     91
     92>>> obj1.normal.delete()
     93>>> obj2.normal.delete()
     94>>> obj3.default.delete()
     95"""}
  • tests/modeltests/model_forms/models.py

     
    1111import tempfile
    1212
    1313from django.db import models
     14from django.core.filestorage.filesystem import FileSystemStorage
    1415
     16temp_storage = FileSystemStorage(tempfile.gettempdir())
     17
    1518ARTICLE_STATUS = (
    1619    (1, 'Draft'),
    1720    (2, 'Pending'),
     
    6063
    6164class TextFile(models.Model):
    6265    description = models.CharField(max_length=20)
    63     file = models.FileField(upload_to=tempfile.gettempdir())
     66    file = models.FileField(storage=temp_storage, upload_to='tests')
    6467
    6568    def __unicode__(self):
    6669        return self.description
    6770
    6871class ImageFile(models.Model):
    6972    description = models.CharField(max_length=20)
    70     image = models.FileField(upload_to=tempfile.gettempdir())
     73    image = models.FileField(storage=temp_storage, upload_to='tests')
    7174
    7275    def __unicode__(self):
    7376        return self.description
     
    799802<class 'django.newforms.fields.UploadedFile'>
    800803>>> instance = f.save()
    801804>>> instance.file
    802 u'...test1.txt'
     805<File: .../test1.txt>
    803806
    804807# Edit an instance that already has the file defined in the model. This will not
    805808# save the file again, but leave it exactly as it is.
     
    808811>>> f.is_valid()
    809812True
    810813>>> f.cleaned_data['file']
    811 u'...test1.txt'
     814<File: .../test1.txt>
    812815>>> instance = f.save()
    813816>>> instance.file
    814 u'...test1.txt'
     817<File: .../test1.txt>
    815818
    816819# Delete the current file since this is not done by Django.
    817820
    818 >>> os.unlink(instance.get_file_filename())
     821>>> instance.file.delete()
    819822
    820823# Override the file by uploading a new one.
    821824
     
    824827True
    825828>>> instance = f.save()
    826829>>> instance.file
    827 u'...test2.txt'
     830<File: .../test2.txt>
    828831
    829832>>> instance.delete()
    830833
     
    836839True
    837840>>> instance = f.save()
    838841>>> instance.file
    839 ''
     842<File: None>
    840843
    841844>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance)
    842845>>> f.is_valid()
    843846True
    844847>>> instance = f.save()
    845848>>> instance.file
    846 u'...test3.txt'
     849<File: .../test3.txt>
    847850>>> instance.delete()
    848851
    849852# ImageField ###################################################################
     
    865868<class 'django.newforms.fields.UploadedFile'>
    866869>>> instance = f.save()
    867870>>> instance.image
    868 u'...test.png'
     871<File: .../test.png>
    869872
    870873# Edit an instance that already has the image defined in the model. This will not
    871874# save the image again, but leave it exactly as it is.
     
    874877>>> f.is_valid()
    875878True
    876879>>> f.cleaned_data['image']
    877 u'...test.png'
     880<File: .../test.png>
    878881>>> instance = f.save()
    879882>>> instance.image
    880 u'...test.png'
     883<File: .../test.png>
    881884
    882885# Delete the current image since this is not done by Django.
    883886
    884 >>> os.unlink(instance.get_image_filename())
     887>>> instance.image.delete()
    885888
    886889# Override the file by uploading a new one.
    887890
     
    890893True
    891894>>> instance = f.save()
    892895>>> instance.image
    893 u'...test2.png'
     896<File: .../test2.png>
    894897
    895898>>> instance.delete()
    896899
     
    902905True
    903906>>> instance = f.save()
    904907>>> instance.image
    905 ''
     908<File: None>
    906909
    907910>>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance)
    908911>>> f.is_valid()
    909912True
    910913>>> instance = f.save()
    911914>>> instance.image
    912 u'...test3.png'
     915<File: .../test3.png>
    913916>>> instance.delete()
    914917
    915918"""}
  • tests/regressiontests/bug639/models.py

     
    11import tempfile
     2
    23from django.db import models
     4from django.core.filestorage.filesystem import FileSystemStorage
    35
     6temp_storage = FileSystemStorage(tempfile.gettempdir())
     7
    48class Photo(models.Model):
    59    title = models.CharField(max_length=30)
    6     image = models.FileField(upload_to=tempfile.gettempdir())
     10    image = models.FileField(storage=temp_storage, upload_to='tests')
    711   
    812    # Support code for the tests; this keeps track of how many times save() gets
    913    # called on each instance.
    1014    def __init__(self, *args, **kwargs):
    11        super(Photo, self).__init__(*args, **kwargs)
    12        self._savecount = 0
     15        super(Photo, self).__init__(*args, **kwargs)
     16        self._savecount = 0
    1317   
    1418    def save(self):
    1519        super(Photo, self).save()
    16         self._savecount +=1
    17  No newline at end of file
     20        self._savecount += 1
     21 No newline at end of file
  • tests/regressiontests/bug639/tests.py

     
    3939        Make sure to delete the "uploaded" file to avoid clogging /tmp.
    4040        """
    4141        p = Photo.objects.get()
    42         os.unlink(p.get_image_filename())
    43  No newline at end of file
     42        p.image.delete(save=False)
  • tests/regressiontests/serializers_regress/models.py

     
    157157class EmailPKData(models.Model):
    158158    data = models.EmailField(primary_key=True)
    159159
    160 class FilePKData(models.Model):
    161     data = models.FileField(primary_key=True, upload_to='/foo/bar')
     160# class FilePKData(models.Model):
     161#    data = models.FileField(primary_key=True, upload_to='/foo/bar')
    162162
    163163class FilePathPKData(models.Model):
    164164    data = models.FilePathField(primary_key=True)
  • tests/regressiontests/serializers_regress/tests.py

     
    125125    (data_obj, 41, EmailData, None),
    126126    (data_obj, 42, EmailData, ""),
    127127    (data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'),
    128     (data_obj, 51, FileData, None),
     128#     (data_obj, 51, FileData, None),
    129129    (data_obj, 52, FileData, ""),
    130130    (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"),
    131131    (data_obj, 61, FilePathData, None),
     
    223223#     (pk_obj, 620, DatePKData, datetime.date(2006,6,16)),
    224224#     (pk_obj, 630, DateTimePKData, datetime.datetime(2006,6,16,10,42,37)),
    225225    (pk_obj, 640, EmailPKData, "hovercraft@example.com"),
    226     (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
     226#     (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
    227227    (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"),
    228228    (pk_obj, 670, DecimalPKData, decimal.Decimal('12.345')),
    229229    (pk_obj, 671, DecimalPKData, decimal.Decimal('-12.345')),
  • tests/regressiontests/storage/__init__.py

    Property changes on: tests/regressiontests/storage
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
     
     1
  • tests/regressiontests/storage/models.py

     
     1# Empty file to force tests to run
     2 No newline at end of file
  • tests/regressiontests/storage/tests.py

     
     1"""
     2Tests for the file storage mechanism
     3
     4>>> import tempfile
     5>>> from django.core.filestorage.filesystem import FileSystemStorage
     6
     7# Instantiate a storage system manually, specifying a location.
     8
     9>>> temp_storage = FileSystemStorage(tempfile.gettempdir())
     10
     11# Standard file access options are available, and work as expected.
     12
     13>>> temp_storage.exists('storage_test')
     14False
     15>>> file = temp_storage.open('storage_test', 'w')
     16>>> file.write('storage contents')
     17>>> file.close()
     18
     19>>> temp_storage.exists('storage_test')
     20True
     21>>> file = temp_storage.open('storage_test', 'r')
     22>>> file.read()
     23'storage contents'
     24>>> file.close()
     25
     26>>> temp_storage.delete('storage_test')
     27>>> temp_storage.exists('storage_test')
     28False
     29
     30# Files can only be accessed if they're below the specified location.
     31
     32>>> temp_storage.exists('..')
     33Traceback (most recent call last):
     34...
     35SuspiciousOperation: Attempted access to '..' denied.
     36>>> temp_storage.open('/etc/passwd')
     37Traceback (most recent call last):
     38  ...
     39SuspiciousOperation: Attempted access to '/etc/passwd' denied.
     40
     41# RemoteFile allows files to be committed by way of a user-defined function.
     42
     43>>> from django.core.filestorage.base import RemoteFile
     44>>> def write_file(contents):
     45...     print 'Writing %s' % contents
     46
     47# Opening for read access doesn't commit back to the server
     48
     49>>> file = RemoteFile('', 'r', write_file)
     50>>> file.close()
     51
     52# The same goes for opening for write access, but not actually writing
     53
     54>>> file = RemoteFile('', 'w', write_file)
     55>>> file.close()
     56
     57# But once it's written to, it gets committed on close
     58
     59>>> file = RemoteFile('', 'w', write_file)
     60>>> file.write('remote contents') # Content isn't committed yet
     61>>> file.close() # Content gets committed to the storage system
     62Writing remote contents
     63
     64# Custom storage systems can be created to customize behavior
     65
     66>>> class CustomStorage(FileSystemStorage):
     67...     def get_available_filename(self, filename):
     68...         # Append numbers to duplicate files rather than underscores, like Trac
     69...
     70...         parts = filename.split('.')
     71...         basename, ext = parts[0], parts[1:]
     72...         number = 2
     73...
     74...         while self.exists(filename):
     75...             filename = '.'.join([basename, str(number)] + ext)
     76...             number += 1
     77...
     78...         return filename
     79>>> custom_storage = CustomStorage(tempfile.gettempdir())
     80
     81>>> first = custom_storage.save('custom_storage', 'custom contents')
     82>>> first
     83u'custom_storage'
     84>>> second = custom_storage.save('custom_storage', 'more contents')
     85>>> second
     86u'custom_storage.2'
     87>>> custom_storage.delete(first)
     88>>> custom_storage.delete(second)
     89"""
     90 No newline at end of file
Back to Top