Ticket #5361: 5361-r8156.diff

File 5361-r8156.diff, 100.6 KB (added by Marty Alchin, 16 years ago)

(hopefully final) patch for review, with full docs and expanded tests

  • django/conf/global_settings.py

     
    226226# Path to the "jing" executable -- needed to validate XMLFields
    227227JING_PATH = "/usr/bin/jing"
    228228
     229# Default file storage mechanism that holds media.
     230DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
     231
    229232# Absolute path to the directory that holds media.
    230233# Example: "/home/media/media.lawrence.com/"
    231234MEDIA_ROOT = ''
  • django/core/files/__init__.py

     
     1import os
     2
     3from django.core.files.base import File
     4from django.core.files.storage import Storage, open
  • django/core/files/base.py

     
     1import os
     2
     3from django.utils.encoding import smart_str, smart_unicode
     4
     5try:
     6    from cStringIO import StringIO
     7except ImportError:
     8    from StringIO import StringIO
     9
     10class File(object):
     11    DEFAULT_CHUNK_SIZE = 64 * 2**10
     12
     13    def __init__(self, file):
     14        self.file = file
     15        self._name = file.name
     16        self._mode = file.mode
     17        self._closed = False
     18
     19    def __str__(self):
     20        return smart_str(self.name or '')
     21
     22    def __unicode__(self):
     23        return smart_unicode(self.name or u'')
     24
     25    def __repr__(self):
     26        return "<%s: %s>" % (self.__class__.__name__, self or "None")
     27
     28    def __nonzero__(self):
     29        return not not self.name
     30
     31    def __len__(self):
     32        return self.size
     33
     34    def _get_name(self):
     35        return self._name
     36    name = property(_get_name)
     37
     38    def _get_mode(self):
     39        return self._mode
     40    mode = property(_get_mode)
     41
     42    def _get_closed(self):
     43        return self._closed
     44    closed = property(_get_closed)
     45
     46    def _get_size(self):
     47        if not hasattr(self, '_size'):
     48            if hasattr(self.file, 'size'):
     49                self._size = self.file.size
     50            elif os.path.exists(self.file.name):
     51                self._size = os.path.getsize(self.file.name)
     52            else:
     53                raise AttributeError("Unable to determine the file's size.")
     54        return self._size
     55
     56    def _set_size(self, size):
     57        self._size = size
     58
     59    size = property(_get_size, _set_size)
     60
     61    def chunks(self, chunk_size=None):
     62        """
     63        Read the file and yield chucks of ``chunk_size`` bytes (defaults to
     64        ``UploadedFile.DEFAULT_CHUNK_SIZE``).
     65        """
     66        if not chunk_size:
     67            chunk_size = self.__class__.DEFAULT_CHUNK_SIZE
     68
     69        if hasattr(self, 'seek'):
     70            self.seek(0)
     71        # Assume the pointer is at zero...
     72        counter = self.size
     73
     74        while counter > 0:
     75            yield self.read(chunk_size)
     76            counter -= chunk_size
     77
     78    def multiple_chunks(self, chunk_size=None):
     79        """
     80        Returns ``True`` if you can expect multiple chunks.
     81
     82        NB: If a particular file representation is in memory, subclasses should
     83        always return ``False`` -- there's no good reason to read from memory in
     84        chunks.
     85        """
     86        if not chunk_size:
     87            chunk_size = self.DEFAULT_CHUNK_SIZE
     88        return self.size > chunk_size
     89
     90    def xreadlines(self):
     91        return iter(self)
     92
     93    def readlines(self):
     94        return list(self.xreadlines())
     95
     96    def __iter__(self):
     97        # Iterate over this file-like object by newlines
     98        buffer_ = None
     99        for chunk in self.chunks():
     100            chunk_buffer = StringIO(chunk)
     101
     102            for line in chunk_buffer:
     103                if buffer_:
     104                    line = buffer_ + line
     105                    buffer_ = None
     106
     107                # If this is the end of a line, yield
     108                # otherwise, wait for the next round
     109                if line[-1] in ('\n', '\r'):
     110                    yield line
     111                else:
     112                    buffer_ = line
     113
     114        if buffer_ is not None:
     115            yield buffer_
     116
     117    def open(self, mode='rb'):
     118        if not self.closed:
     119            self.seek(0)
     120        elif os.path.exists(self.file.name):
     121            self.file = open(self.file.name, self.file.mode)
     122        else:
     123            raise ValueError("The file cannot be reopened.")
     124
     125    def seek(self, position):
     126        self.file.seek(position)
     127
     128    def tell(self):
     129        return self.file.tell()
     130
     131    def read(self, num_bytes=None):
     132        if num_bytes is None:
     133            return self.file.read()
     134        return self.file.read(num_bytes)
     135
     136    def write(self, content):
     137        if not self.mode.startswith('w'):
     138            raise IOError("File was not opened with write access.")
     139        self.file.write(content)
     140
     141    def flush(self):
     142        if not self.mode.startswith('w'):
     143            raise IOError("File was not opened with write access.")
     144        self.file.flush()
     145
     146    def close(self):
     147        self.file.close()
     148        self._closed = True
     149
     150class ContentFile(File):
     151    """
     152    A File-like object that takes just raw content, rather than an actual file.
     153    """
     154    def __init__(self, content):
     155        self.file = StringIO(content or '')
     156        self.size = len(content or '')
     157        self.file.seek(0)
     158        self._closed = False
     159
     160    def __str__(self):
     161        return 'Raw content'
     162
     163    def __nonzero__(self):
     164        return True
     165
     166    def open(self, mode=None):
     167        if self._closed:
     168            self._closed = False
     169        self.seek(0)
  • django/core/files/images.py

     
     1"""
     2Utility functions for handling images.
     3
     4Requires PIL, as you might imagine.
     5"""
     6
     7from PIL import ImageFile as PIL
     8from django.core.files import File
     9
     10class ImageFile(File):
     11    """
     12    A mixin for use alongside django.core.files.base.File, which provides
     13    additional features for dealing with images.
     14    """
     15    def _get_width(self):
     16        return self._get_image_dimensions()[0]
     17    width = property(_get_width)
     18
     19    def _get_height(self):
     20        return self._get_image_dimensions()[1]
     21    height = property(_get_height)
     22
     23    def _get_image_dimensions(self):
     24        if not hasattr(self, '_dimensions_cache'):
     25            self._dimensions_cache = get_image_dimensions(self)
     26        return self._dimensions_cache
     27
     28def get_image_dimensions(file_or_path):
     29    """Returns the (width, height) of an image, given an open file or a path."""
     30    p = PIL.Parser()
     31    if hasattr(file_or_path, 'read'):
     32        file = file_or_path
     33    else:
     34        file = open(file_or_path, 'rb')
     35    while 1:
     36        data = file.read(1024)
     37        if not data:
     38            break
     39        p.feed(data)
     40        if p.image:
     41            return p.image.size
     42    return None
  • django/core/files/remote.py

     
     1from StringIO import StringIO
     2
     3class RemoteFile(StringIO):
     4    """Sends files to remote storage automatically, when necessary."""
     5
     6    def __init__(self, data, mode, writer):
     7        self._mode = mode
     8        self._write_to_storage = writer
     9        self._is_dirty = False
     10        StringIO.__init__(self, data)
     11
     12    def write(self, data):
     13        if 'w' not in self._mode:
     14            raise AttributeError("File was opened for read-only access.")
     15        StringIO.write(self, data)
     16        self._is_dirty = True
     17
     18    def close(self):
     19        if self._is_dirty:
     20            self._write_to_storage(self.getvalue())
     21        StringIO.close(self)
  • django/core/files/storage.py

     
     1import os
     2import urlparse
     3from __builtin__ import open as python_open
     4
     5from django.conf import settings
     6from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
     7from django.utils.encoding import force_unicode, smart_str
     8from django.utils.text import force_unicode, get_valid_filename
     9from django.utils._os import safe_join
     10from django.core.files import locks, File
     11
     12__all__ = ('StorageBase', 'Storage', 'FileSystemStorage', 'open')
     13
     14class StorageBase(object):
     15    """
     16    A base storage class, providing some default behaviors that all other
     17    storage systems can inherit or override, as necessary.
     18    """
     19   
     20    # The following methods represent a public interface to private methods.
     21    # These shouldn't be overridden by subclasses unless absolutely necessary.
     22
     23    def open(self, name, mode='rb', mixin=None):
     24        """
     25        Retrieves the specified file from storage, using the optional mixin
     26        class to customize what features are available on the File returned.
     27        """
     28        file = self._open(name, mode)
     29        if mixin:
     30            # Add the mixin as a parent class of the File returned from storage.
     31            file.__class__ = type(mixin.__name__, (mixin, file.__class__), {})
     32        return file
     33
     34    def save(self, name, content):
     35        """
     36        Saves new content to the file specified by name. The content should be a
     37        proper File object, ready to be read from the beginning.
     38        """
     39        # Check for old-style usage. Warn here first since there are multiple
     40        # locations where we need to support both new and old usage.
     41        if isinstance(content, basestring):
     42            import warnings
     43            warnings.warn(
     44                message = "Representing files as strings is deprecated." \
     45                          "Use django.core.files.base.ContentFile instead.",
     46                category = DeprecationWarning,
     47                stacklevel = 2
     48            )
     49            from django.core.files.base import ContentFile
     50            content = ContentFile(content)
     51
     52        # Get the proper name for the file, as it will actually be saved.
     53        if name is None:
     54            name = content.name
     55        name = self.get_available_name(name)
     56
     57        self._save(name, content)
     58
     59        # Store filenames with forward slashes, even on Windows
     60        return force_unicode(name.replace('\\', '/'))
     61
     62    # These methods are part of the public API, with default implementations.
     63
     64    def get_valid_name(self, name):
     65        """
     66        Returns a filename, based on the provided filename, that's suitable for
     67        use in the target storage system.
     68        """
     69        return get_valid_filename(name)
     70
     71    def get_available_name(self, name):
     72        """
     73        Returns a filename that's free on the target storage system, and
     74        available for new content to be written to.
     75        """
     76        # If the filename already exists, keep adding an underscore to the name
     77        # of the file until the filename doesn't exist.
     78        while self.exists(name):
     79            try:
     80                dot_index = name.rindex('.')
     81            except ValueError: # filename has no dot
     82                name += '_'
     83            else:
     84                name = name[:dot_index] + '_' + name[dot_index:]
     85        return name
     86
     87    def path(self, name):
     88        """
     89        Returns a local filesystem path where the file can be retrieved using
     90        Python's built-in open() function. Storage systems that can't be
     91        accessed using open() should *not* implement this method.
     92        """
     93        raise NotImplementedError("This backend doesn't support absolute paths.")
     94
     95    # The following methods form the public API for storage systems, but with
     96    # no default implementations. Subclasses must implement *all* of these.
     97
     98    def delete(self, name):
     99        """
     100        Deletes the specified file from the storage system.
     101        """
     102        raise NotImplementedError()
     103
     104    def exists(self, name):
     105        """
     106        Returns True if a file referened by the given name already exists in the
     107        storage system, or False if the name is available for a new file.
     108        """
     109        raise NotImplementedError()
     110
     111    def listdir(self, path):
     112        """
     113        Lists the contents of the specified path, returning a 2-tuple of lists;
     114        the first item being directories, the second item being files.
     115        """
     116        raise NotImplementedError()
     117
     118    def size(self, name):
     119        """
     120        Returns the total size, in bytes, of the file specified by name.
     121        """
     122        raise NotImplementedError()
     123
     124    def url(self, name):
     125        """
     126        Returns an absolute URL where the file's contents can be accessed
     127        directly by a web browser.
     128        """
     129        raise NotImplementedError()
     130
     131class FileSystemStorage(StorageBase):
     132    """
     133    Standard filesystem storage
     134    """
     135
     136    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
     137        self.location = os.path.abspath(location)
     138        self.base_url = base_url
     139
     140    def _open(self, name, mode='rb'):
     141        return File(python_open(self.path(name), mode))
     142
     143    def _save(self, name, content):
     144        full_path = self.path(name)
     145
     146        directory = os.path.dirname(full_path)
     147        if not os.path.exists(directory):
     148            os.makedirs(directory)
     149        elif not os.path.isdir(directory):
     150            raise IOError("%s exists and is not a directory." % directory)
     151
     152        if hasattr(content, 'temporary_file_path'):
     153            # This file has a file path that we can move.
     154            content.close()
     155            file_move_safe(content.temporary_file_path(), full_path)
     156        else:
     157            # This is a normal uploadedfile that we can stream.
     158            fp = python_open(full_path, 'wb')
     159            locks.lock(fp, locks.LOCK_EX)
     160            for chunk in content.chunks():
     161                fp.write(chunk)
     162            locks.unlock(fp)
     163            fp.close()
     164
     165    def delete(self, name):
     166        name = self.path(name)
     167        # If the file exists, delete it from the filesystem.
     168        if os.path.exists(name):
     169            os.remove(name)
     170
     171    def exists(self, name):
     172        return os.path.exists(self.path(name))
     173
     174    def listdir(self, path):
     175        path = self.path(path)
     176        directories, files = [], []
     177        for entry in os.listdir(path):
     178            if os.path.isdir(os.path.join(path, entry)):
     179                directories.append(entry)
     180            else:
     181                files.append(entry)
     182        return directories, files
     183
     184    def path(self, name):
     185        try:
     186            path = safe_join(self.location, name)
     187        except ValueError:
     188            raise SuspiciousOperation("Attempted access to '%s' denied." % name)
     189        return os.path.normpath(path)
     190
     191    def size(self, name):
     192        return os.path.getsize(self.path(name))
     193
     194    def url(self, name):
     195        if self.base_url is None:
     196            raise ValueError("This file is not accessible via a URL.")
     197        return urlparse.urljoin(self.base_url, name).replace('\\', '/')
     198
     199class StorageFile(File):
     200    def __init__(self, reference):
     201        self.reference = reference
     202        self.open()
     203
     204    def open(self):
     205        if self.closed:
     206            self.closed = False
     207        self.seek(0)
     208
     209    def seek(self, position):
     210        self.position = position
     211
     212    def tell(self):
     213        return self.position
     214
     215    def read(self, num_bytes=None):
     216        raise NotImplementedError("")
     217
     218    def write(self, content):
     219        raise NotImplementedError("")
     220
     221    def close(self):
     222        self._closed = True
     223
     224def get_storage_class(import_path):
     225    try:
     226        dot = import_path.rindex('.')
     227    except ValueError:
     228        raise ImproperlyConfigured("%s isn't a storage module." % import_path)
     229    module, classname = import_path[:dot], import_path[dot+1:]
     230    try:
     231        mod = __import__(module, {}, {}, [''])
     232    except ImportError, e:
     233        raise ImproperlyConfigured('Error importing storage module %s: "%s"' % (module, e))
     234    try:
     235        return getattr(mod, classname)
     236    except AttributeError:
     237        raise ImproperlyConfigured('Storage module "%s" does not define a "%s" class.' % (module, classname))
     238
     239Storage = get_storage_class(settings.DEFAULT_FILE_STORAGE)
     240default_storage = Storage()
     241
     242def open(path, mode='rb', storage=None, mixin=None):
     243    """
     244    A more intelligent open() than the one that comes with Python, working with
     245    various storage systems and providing a more fully-featured File object.
     246    """
     247    if storage is None:
     248        # In keeping Python's own open(), if no storage system is supplied, it
     249        # looks at the local filesystem. Since FileSystemStorage protects from
     250        # access outside of its base location, though, this needs to instantiate
     251        # a storage object speifically for the target file.
     252        storage = FileSystemStorage(location=os.path.dirname(path))
     253    return storage.open(path, mode, mixin=mixin)
  • django/core/files/uploadedfile.py

     
    1010    from StringIO import StringIO
    1111
    1212from django.conf import settings
     13from django.core.files.base import File
    1314
    1415from django.core.files import temp as tempfile
    1516
     
    3940    else:
    4041        return property(getter, setter)
    4142
    42 class UploadedFile(object):
     43class UploadedFile(File):
    4344    """
    4445    A abstract uploaded file (``TemporaryUploadedFile`` and
    4546    ``InMemoryUploadedFile`` are the built-in concrete subclasses).
     
    7677
    7778    name = property(_get_name, _set_name)
    7879
    79     def chunks(self, chunk_size=None):
    80         """
    81         Read the file and yield chucks of ``chunk_size`` bytes (defaults to
    82         ``UploadedFile.DEFAULT_CHUNK_SIZE``).
    83         """
    84         if not chunk_size:
    85             chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE
    86 
    87         if hasattr(self, 'seek'):
    88             self.seek(0)
    89         # Assume the pointer is at zero...
    90         counter = self.size
    91 
    92         while counter > 0:
    93             yield self.read(chunk_size)
    94             counter -= chunk_size
    95 
    9680    # Deprecated properties
    9781    filename = deprecated_property(old="filename", new="name")
    9882    file_name = deprecated_property(old="file_name", new="name")
     
    10892        return self.read()
    10993    data = property(_get_data)
    11094
    111     def multiple_chunks(self, chunk_size=None):
    112         """
    113         Returns ``True`` if you can expect multiple chunks.
    114 
    115         NB: If a particular file representation is in memory, subclasses should
    116         always return ``False`` -- there's no good reason to read from memory in
    117         chunks.
    118         """
    119         if not chunk_size:
    120             chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE
    121         return self.size > chunk_size
    122 
    12395    # Abstract methods; subclasses *must* define read() and probably should
    12496    # define open/close.
    12597    def read(self, num_bytes=None):
     
    131103    def close(self):
    132104        pass
    133105
    134     def xreadlines(self):
    135         return self
    136 
    137     def readlines(self):
    138         return list(self.xreadlines())
    139 
    140     def __iter__(self):
    141         # Iterate over this file-like object by newlines
    142         buffer_ = None
    143         for chunk in self.chunks():
    144             chunk_buffer = StringIO(chunk)
    145 
    146             for line in chunk_buffer:
    147                 if buffer_:
    148                     line = buffer_ + line
    149                     buffer_ = None
    150 
    151                 # If this is the end of a line, yield
    152                 # otherwise, wait for the next round
    153                 if line[-1] in ('\n', '\r'):
    154                     yield line
    155                 else:
    156                     buffer_ = line
    157 
    158         if buffer_ is not None:
    159             yield buffer_
    160 
    161106    # Backwards-compatible support for uploaded-files-as-dictionaries.
    162107    def __getitem__(self, key):
    163108        warnings.warn(
  • django/db/models/__init__.py

     
    88from django.db.models.base import Model
    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
    1314
  • django/db/models/base.py

     
    33import sys
    44import os
    55from itertools import izip
     6from warnings import warn
    67try:
    78    set
    89except NameError:
     
    1213import django.db.models.manager         # Ditto.
    1314from django.core import validators
    1415from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
    15 from django.db.models.fields import AutoField, ImageField
     16from django.db.models.fields import AutoField
    1617from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
    1718from django.db.models.query import delete_objects, Q, CollectedObjects
    1819from django.db.models.options import Options
     
    463464        return getattr(self, cachename)
    464465
    465466    def _get_FIELD_filename(self, field):
    466         if getattr(self, field.attname): # Value is not blank.
    467             return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)))
    468         return ''
     467        warn("instance.get_%s_filename() is deprecated. Use instance.%s.path instead." % \
     468            (field.attname, field.attname), DeprecationWarning, stacklevel=3)
     469        try:
     470            return getattr(self, field.attname).path
     471        except ValueError:
     472            return ''
    469473
    470474    def _get_FIELD_url(self, field):
    471         if getattr(self, field.attname): # Value is not blank.
    472             import urlparse
    473             return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
    474         return ''
     475        warn("instance.get_%s_url() is deprecated. Use instance.%s.url instead." % \
     476            (field.attname, field.attname), DeprecationWarning, stacklevel=3)
     477        try:
     478            return getattr(self, field.attname).url
     479        except ValueError:
     480            return ''
    475481
    476482    def _get_FIELD_size(self, field):
    477         return os.path.getsize(self._get_FIELD_filename(field))
     483        warn("instance.get_%s_size() is deprecated. Use instance.%s.size instead." % \
     484            (field.attname, field.attname), DeprecationWarning, stacklevel=3)
     485        return getattr(self, field.attname).size
    478486
    479     def _save_FIELD_file(self, field, filename, raw_field, save=True):
    480         # Create the upload directory if it doesn't already exist
    481         directory = os.path.join(settings.MEDIA_ROOT, field.get_directory_name())
    482         if not os.path.exists(directory):
    483             os.makedirs(directory)
    484         elif not os.path.isdir(directory):
    485             raise IOError('%s exists and is not a directory' % directory)       
     487    def _save_FIELD_file(self, field, filename, content, save=True):
     488        warn("instance.save_%s_file() is deprecated. Use instance.%s.save() instead." % \
     489            (field.attname, field.attname), DeprecationWarning, stacklevel=3)
     490        return getattr(self, field.attname).save(filename, content, save)
    486491
    487         # Check for old-style usage (files-as-dictionaries). Warn here first
    488         # since there are multiple locations where we need to support both new
    489         # and old usage.
    490         if isinstance(raw_field, dict):
    491             import warnings
    492             warnings.warn(
    493                 message = "Representing uploaded files as dictionaries is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.",
    494                 category = DeprecationWarning,
    495                 stacklevel = 2
    496             )
    497             from django.core.files.uploadedfile import SimpleUploadedFile
    498             raw_field = SimpleUploadedFile.from_dict(raw_field)
    499 
    500         elif isinstance(raw_field, basestring):
    501             import warnings
    502             warnings.warn(
    503                 message = "Representing uploaded files as strings is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.",
    504                 category = DeprecationWarning,
    505                 stacklevel = 2
    506             )
    507             from django.core.files.uploadedfile import SimpleUploadedFile
    508             raw_field = SimpleUploadedFile(filename, raw_field)
    509 
    510         if filename is None:
    511             filename = raw_field.file_name
    512 
    513         filename = field.get_filename(filename)
    514 
    515         # If the filename already exists, keep adding an underscore to the name
    516         # of the file until the filename doesn't exist.
    517         while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
    518             try:
    519                 dot_index = filename.rindex('.')
    520             except ValueError: # filename has no dot.
    521                 filename += '_'
    522             else:
    523                 filename = filename[:dot_index] + '_' + filename[dot_index:]
    524 
    525         # Save the file name on the object and write the file to disk.
    526         setattr(self, field.attname, filename)
    527         full_filename = self._get_FIELD_filename(field)
    528         if hasattr(raw_field, 'temporary_file_path'):
    529             # This file has a file path that we can move.
    530             raw_field.close()
    531             file_move_safe(raw_field.temporary_file_path(), full_filename)
    532         else:
    533             # This is a normal uploadedfile that we can stream.
    534             fp = open(full_filename, 'wb')
    535             locks.lock(fp, locks.LOCK_EX)
    536             for chunk in raw_field.chunks():
    537                 fp.write(chunk)
    538             locks.unlock(fp)
    539             fp.close()
    540 
    541         # Save the width and/or height, if applicable.
    542         if isinstance(field, ImageField) and \
    543                 (field.width_field or field.height_field):
    544             from django.utils.images import get_image_dimensions
    545             width, height = get_image_dimensions(full_filename)
    546             if field.width_field:
    547                 setattr(self, field.width_field, width)
    548             if field.height_field:
    549                 setattr(self, field.height_field, height)
    550 
    551         # Save the object because it has changed, unless save is False.
    552         if save:
    553             self.save()
    554 
    555492    _save_FIELD_file.alters_data = True
    556493
    557494    def _get_FIELD_width(self, field):
    558         return self._get_image_dimensions(field)[0]
     495        warn("instance.get_%s_width() is deprecated. Use instance.%s.width instead." % \
     496            (field.attname, field.attname), DeprecationWarning, stacklevel=3)
     497        return getattr(self, field.attname).width()
    559498
    560499    def _get_FIELD_height(self, field):
    561         return self._get_image_dimensions(field)[1]
     500        warn("instance.get_%s_height() is deprecated. Use instance.%s.height instead." % \
     501            (field.attname, field.attname), DeprecationWarning, stacklevel=3)
     502        return getattr(self, field.attname).height()
    562503
    563     def _get_image_dimensions(self, field):
    564         cachename = "__%s_dimensions_cache" % field.name
    565         if not hasattr(self, cachename):
    566             from django.utils.images import get_image_dimensions
    567             filename = self._get_FIELD_filename(field)
    568             setattr(self, cachename, get_image_dimensions(filename))
    569         return getattr(self, cachename)
    570504
    571 
    572505############################################
    573506# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
    574507############################################
  • django/db/models/fields/__init__.py

     
    88    from django.utils import _decimal as decimal    # for Python 2.3
    99
    1010from django.db import connection, get_creation_module
    11 from django.db.models import signals
    1211from django.db.models.query_utils import QueryWrapper
    1312from django.dispatch import dispatcher
    1413from django.conf import settings
     
    763762        defaults.update(kwargs)
    764763        return super(EmailField, self).formfield(**defaults)
    765764
    766 class FileField(Field):
    767     def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
    768         self.upload_to = upload_to
    769         kwargs['max_length'] = kwargs.get('max_length', 100)
    770         Field.__init__(self, verbose_name, name, **kwargs)
    771 
    772     def get_internal_type(self):
    773         return "FileField"
    774 
    775     def get_db_prep_value(self, value):
    776         "Returns field's value prepared for saving into a database."
    777         # Need to convert UploadedFile objects provided via a form to unicode for database insertion
    778         if hasattr(value, 'name'):
    779             return value.name
    780         elif value is None:
    781             return None
    782         else:
    783             return unicode(value)
    784 
    785     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    786         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
    787         if not self.blank:
    788             if rel:
    789                 # This validator makes sure FileFields work in a related context.
    790                 class RequiredFileField(object):
    791                     def __init__(self, other_field_names, other_file_field_name):
    792                         self.other_field_names = other_field_names
    793                         self.other_file_field_name = other_file_field_name
    794                         self.always_test = True
    795                     def __call__(self, field_data, all_data):
    796                         if not all_data.get(self.other_file_field_name, False):
    797                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))
    798                             c(field_data, all_data)
    799                 # First, get the core fields, if any.
    800                 core_field_names = []
    801                 for f in opts.fields:
    802                     if f.core and f != self:
    803                         core_field_names.extend(f.get_manipulator_field_names(name_prefix))
    804                 # Now, if there are any, add the validator to this FormField.
    805                 if core_field_names:
    806                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
    807             else:
    808                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))
    809                 v.always_test = True
    810                 field_list[0].validator_list.append(v)
    811                 field_list[0].is_required = field_list[1].is_required = False
    812 
    813         # If the raw path is passed in, validate it's under the MEDIA_ROOT.
    814         def isWithinMediaRoot(field_data, all_data):
    815             f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
    816             if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
    817                 raise validators.ValidationError, _("Enter a valid filename.")
    818         field_list[1].validator_list.append(isWithinMediaRoot)
    819         return field_list
    820 
    821     def contribute_to_class(self, cls, name):
    822         super(FileField, self).contribute_to_class(cls, name)
    823         setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
    824         setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
    825         setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
    826         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save))
    827         dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
    828 
    829     def delete_file(self, instance):
    830         if getattr(instance, self.attname):
    831             file_name = getattr(instance, 'get_%s_filename' % self.name)()
    832             # If the file exists and no other object of this type references it,
    833             # delete it from the filesystem.
    834             if os.path.exists(file_name) and \
    835                 not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}):
    836                 os.remove(file_name)
    837 
    838     def get_manipulator_field_objs(self):
    839         return [oldforms.FileUploadField, oldforms.HiddenField]
    840 
    841     def get_manipulator_field_names(self, name_prefix):
    842         return [name_prefix + self.name + '_file', name_prefix + self.name]
    843 
    844     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    845         upload_field_name = self.get_manipulator_field_names('')[0]
    846         if new_data.get(upload_field_name, False):
    847             if rel:
    848                 file = new_data[upload_field_name][0]
    849             else:
    850                 file = new_data[upload_field_name]
    851 
    852             if not file:
    853                 return
    854 
    855             # Backwards-compatible support for files-as-dictionaries.
    856             # We don't need to raise a warning because Model._save_FIELD_file will
    857             # do so for us.
    858             try:
    859                 file_name = file.name
    860             except AttributeError:
    861                 file_name = file['filename']
    862 
    863             func = getattr(new_object, 'save_%s_file' % self.name)
    864             func(file_name, file, save)
    865 
    866     def get_directory_name(self):
    867         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
    868 
    869     def get_filename(self, filename):
    870         from django.utils.text import get_valid_filename
    871         f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
    872         return os.path.normpath(f)
    873 
    874     def save_form_data(self, instance, data):
    875         from django.core.files.uploadedfile import UploadedFile
    876         if data and isinstance(data, UploadedFile):
    877             getattr(instance, "save_%s_file" % self.name)(data.name, data, save=False)
    878 
    879     def formfield(self, **kwargs):
    880         defaults = {'form_class': forms.FileField}
    881         # If a file has been provided previously, then the form doesn't require
    882         # that a new file is provided this time.
    883         # The code to mark the form field as not required is used by
    884         # form_for_instance, but can probably be removed once form_for_instance
    885         # is gone. ModelForm uses a different method to check for an existing file.
    886         if 'initial' in kwargs:
    887             defaults['required'] = False
    888         defaults.update(kwargs)
    889         return super(FileField, self).formfield(**defaults)
    890 
    891765class FilePathField(Field):
    892766    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
    893767        self.path, self.match, self.recursive = path, match, recursive
     
    929803        defaults.update(kwargs)
    930804        return super(FloatField, self).formfield(**defaults)
    931805
    932 class ImageField(FileField):
    933     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
    934         self.width_field, self.height_field = width_field, height_field
    935         FileField.__init__(self, verbose_name, name, **kwargs)
    936 
    937     def get_manipulator_field_objs(self):
    938         return [oldforms.ImageUploadField, oldforms.HiddenField]
    939 
    940     def contribute_to_class(self, cls, name):
    941         super(ImageField, self).contribute_to_class(cls, name)
    942         # Add get_BLAH_width and get_BLAH_height methods, but only if the
    943         # image field doesn't have width and height cache fields.
    944         if not self.width_field:
    945             setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))
    946         if not self.height_field:
    947             setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
    948 
    949     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    950         FileField.save_file(self, new_data, new_object, original_object, change, rel, save)
    951         # If the image has height and/or width field(s) and they haven't
    952         # changed, set the width and/or height field(s) back to their original
    953         # values.
    954         if change and (self.width_field or self.height_field) and save:
    955             if self.width_field:
    956                 setattr(new_object, self.width_field, getattr(original_object, self.width_field))
    957             if self.height_field:
    958                 setattr(new_object, self.height_field, getattr(original_object, self.height_field))
    959             new_object.save()
    960 
    961     def formfield(self, **kwargs):
    962         defaults = {'form_class': forms.ImageField}
    963         defaults.update(kwargs)
    964         return super(ImageField, self).formfield(**defaults)
    965 
    966806class IntegerField(Field):
    967807    empty_strings_allowed = False
    968808    def get_db_prep_value(self, value):
  • 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.files.base import File, ContentFile
     7from django.core.files.storage import default_storage
     8from django.core.files.images import ImageFile
     9from django.core.files.uploadedfile import UploadedFile
     10from django.utils.functional import curry
     11from django.dispatch import dispatcher
     12from django.db.models import signals
     13from django.utils.encoding import force_unicode, smart_str
     14from django.utils.translation import ugettext_lazy, ugettext as _
     15from django import oldforms
     16from django import forms
     17from django.core import validators
     18from django.db.models.loading import cache
     19
     20class FieldFile(File):
     21    def __init__(self, instance, field, name):
     22        self.instance = instance
     23        self.field = field
     24        self.storage = field.storage
     25        self._name = name or u''
     26        self._closed = False
     27
     28    def __eq__(self, other):
     29        # Older code may be expecting FileField values to be simple strings.
     30        # By overriding the == operator, it can remain backwards compatibility.
     31        if hasattr(other, 'name'):
     32            return self.name == other.name
     33        return self.name == other
     34
     35    # The standard File contains most of the necessary properties, but
     36    # FieldFiles can be instantiated without a name, so that needs to
     37    # be checked for here.
     38
     39    def _require_file(self):
     40        if not self:
     41            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
     42
     43    def _get_file(self):
     44        self._require_file()
     45        if not hasattr(self, '_file'):
     46            self._file = self.storage.open(self.name, 'rb')
     47        return self._file
     48    file = property(_get_file)
     49
     50    def _get_path(self):
     51        self._require_file()
     52        return self.storage.path(self.name)
     53    path = property(_get_path)
     54
     55    def _get_url(self):
     56        self._require_file()
     57        return self.storage.url(self.name)
     58    url = property(_get_url)
     59
     60    def open(self, mode='rb'):
     61        self._require_file()
     62        return super(FieldFile, self).open(mode)
     63    # open() doesn't alter the file's contents, but it does reset the pointer
     64    open.alters_data = True
     65
     66    # In addition to the standard File API, FieldFiles have extra methods
     67    # to further manipulate the underlying file, as well as update the
     68    # associated model instance.
     69
     70    def save(self, name, content, save=True):
     71        name = self.field.generate_filename(self.instance, name)
     72        self._name = self.storage.save(name, content)
     73        setattr(self.instance, self.field.name, self.name)
     74
     75        # Update the filesize cache
     76        self._size = len(content)
     77
     78        # Save the object because it has changed, unless save is False
     79        if save:
     80            self.instance.save()
     81    save.alters_data = True
     82
     83    def delete(self, save=True):
     84        self.close()
     85        self.storage.delete(self.name)
     86
     87        self._name = None
     88        setattr(self.instance, self.field.name, self.name)
     89
     90        # Delete the filesize cache
     91        if hasattr(self, '_size'):
     92            del self._size
     93
     94        if save:
     95            self.instance.save()
     96    delete.alters_data = True
     97
     98    def __getstate__(self):
     99        # FieldFile needs access to its associated model field and an instance
     100        # it's attached to in order to work properly, but the only necessary
     101        # data to be pickled is the file's name itself. Everything else will
     102        # be restored later, by FileDescriptor below.
     103        return {'_name': self.name, '_closed': False}
     104
     105class FileDescriptor(object):
     106    def __init__(self, field):
     107        self.field = field
     108
     109    def __get__(self, instance=None, owner=None):
     110        if instance is None:
     111            raise AttributeError, "%s can only be accessed from %s instances." % (self.field.name(self.owner.__name__))
     112        file = instance.__dict__[self.field.name]
     113        if not isinstance(file, FieldFile):
     114            # Create a new instance of FieldFile, based on a given file name
     115            instance.__dict__[self.field.name] = self.field.attr_class(instance, self.field, file)
     116        elif not hasattr(file, 'field'):
     117            # The FieldFile was pickled, so some attributes need to be reset.
     118            file.instance = instance
     119            file.field = self.field
     120            file.storage = self.field.storage
     121        return instance.__dict__[self.field.name]
     122
     123    def __set__(self, instance, value):
     124        instance.__dict__[self.field.name] = value
     125
     126class FileField(Field):
     127    attr_class = FieldFile
     128
     129    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
     130        for arg in ('core', 'primary_key', 'unique'):
     131            if arg in kwargs:
     132                raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__))
     133
     134        self.storage = storage or default_storage
     135        self.upload_to = upload_to
     136        if callable(upload_to):
     137            self.generate_filename = upload_to
     138
     139        kwargs['max_length'] = kwargs.get('max_length', 100)
     140        super(FileField, self).__init__(verbose_name, name, **kwargs)
     141
     142    def get_internal_type(self):
     143        return "FileField"
     144
     145    def get_db_prep_lookup(self, lookup_type, value):
     146        if hasattr(value, 'name'):
     147            value = value.name
     148        return super(FileField, self).get_db_prep_lookup(lookup_type, value)
     149
     150    def get_db_prep_value(self, value):
     151        "Returns field's value prepared for saving into a database."
     152        # Need to convert File objects provided via a form to unicode for database insertion
     153        if value is None:
     154            return None
     155        return unicode(value.name)
     156
     157    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
     158        field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
     159        if not self.blank:
     160            if rel:
     161                # This validator makes sure FileFields work in a related context.
     162                class RequiredFileField(object):
     163                    def __init__(self, other_field_names, other_file_field_name):
     164                        self.other_field_names = other_field_names
     165                        self.other_file_field_name = other_file_field_name
     166                        self.always_test = True
     167                    def __call__(self, field_data, all_data):
     168                        if not all_data.get(self.other_file_field_name, False):
     169                            c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))
     170                            c(field_data, all_data)
     171                # First, get the core fields, if any.
     172                core_field_names = []
     173                for f in opts.fields:
     174                    if f.core and f != self:
     175                        core_field_names.extend(f.get_manipulator_field_names(name_prefix))
     176                # Now, if there are any, add the validator to this FormField.
     177                if core_field_names:
     178                    field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
     179            else:
     180                v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))
     181                v.always_test = True
     182                field_list[0].validator_list.append(v)
     183                field_list[0].is_required = field_list[1].is_required = False
     184
     185        # If the raw path is passed in, validate it's under the MEDIA_ROOT.
     186        def isWithinMediaRoot(field_data, all_data):
     187            f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
     188            if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
     189                raise validators.ValidationError(_("Enter a valid filename."))
     190        field_list[1].validator_list.append(isWithinMediaRoot)
     191        return field_list
     192
     193    def contribute_to_class(self, cls, name):
     194        super(FileField, self).contribute_to_class(cls, name)
     195        setattr(cls, self.name, FileDescriptor(self))
     196        setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
     197        setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
     198        setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
     199        setattr(cls, 'save_%s_file' % self.name, lambda instance, name, content, save=True: instance._save_FIELD_file(self, name, content, save))
     200        dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
     201
     202    def delete_file(self, instance, sender):
     203        file = getattr(instance, self.attname)
     204        # If no other object of this type references the file,
     205        # and it's not the default value for future objects,
     206        # delete it from the backend.
     207        if file and file.name != self.default and \
     208            not sender._default_manager.filter(**{self.name: file.name}):
     209                file.delete(save=False)
     210        elif file:
     211            # Otherwise, just close the file, so it doesn't tie up resources.
     212            file.close()
     213
     214    def get_manipulator_field_objs(self):
     215        return [oldforms.FileUploadField, oldforms.HiddenField]
     216
     217    def get_manipulator_field_names(self, name_prefix):
     218        return [name_prefix + self.name + '_file', name_prefix + self.name]
     219
     220    def save_file(self, new_data, new_object, original_object, change, rel, save=True):
     221        upload_field_name = self.get_manipulator_field_names('')[0]
     222        if new_data.get(upload_field_name, False):
     223            if rel:
     224                file = new_data[upload_field_name][0]
     225            else:
     226                file = new_data[upload_field_name]
     227
     228            # Backwards-compatible support for files-as-dictionaries.
     229            # We don't need to raise a warning because the storage backend will
     230            # do so for us.
     231            try:
     232                filename = file.name
     233            except AttributeError:
     234                filename = file['filename']
     235            filename = self.get_filename(filename)
     236
     237            getattr(new_object, self.attname).save(filename, file, save)
     238
     239    def get_directory_name(self):
     240        return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
     241
     242    def get_filename(self, filename):
     243        return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))
     244
     245    def generate_filename(self, instance, filename):
     246        return os.path.join(self.get_directory_name(), self.get_filename(filename))
     247
     248    def save_form_data(self, instance, data):
     249        if data and isinstance(data, UploadedFile):
     250            getattr(instance, self.name).save(data.name, data, save=False)
     251
     252    def formfield(self, **kwargs):
     253        defaults = {'form_class': forms.FileField}
     254        # If a file has been provided previously, then the form doesn't require
     255        # that a new file is provided this time.
     256        # The code to mark the form field as not required is used by
     257        # form_for_instance, but can probably be removed once form_for_instance
     258        # is gone. ModelForm uses a different method to check for an existing file.
     259        if 'initial' in kwargs:
     260            defaults['required'] = False
     261        defaults.update(kwargs)
     262        return super(FileField, self).formfield(**defaults)
     263
     264class ImageFieldFile(ImageFile, FieldFile):
     265    def save(self, name, content, save=True):
     266        super(ImageFieldFile, self).save(name, content, save)
     267
     268        # Update the cache for image dimensions
     269        from django.core.files.images import get_image_dimensions
     270        if not hasattr(content, 'read'):
     271            content = ContentFile(name, content)
     272        self._dimensions_cache = get_image_dimensions(content)
     273
     274    def delete(self, save=True):
     275        # Clear the image dimensions cache
     276        if hasattr(self, '_dimensions_cache'):
     277            del self._dimensions_cache
     278
     279        super(ImageFieldFile, self).delete(save)
     280
     281class ImageField(FileField):
     282    attr_class = ImageFieldFile
     283
     284    def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
     285        self.width_field, self.height_field = width_field, height_field
     286        FileField.__init__(self, verbose_name, name, **kwargs)
     287
     288    def get_manipulator_field_objs(self):
     289        return [oldforms.ImageUploadField, oldforms.HiddenField]
     290
     291    def contribute_to_class(self, cls, name):
     292        super(ImageField, self).contribute_to_class(cls, name)
     293        # Add get_BLAH_width and get_BLAH_height methods, but only if the
     294        # image field doesn't have width and height cache fields.
     295        if not self.width_field:
     296            setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))
     297        if not self.height_field:
     298            setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
     299
     300    def save_form_data(self, instance, data):
     301        # If the image has height and/or width field(s) and they haven't
     302        # changed, set the width and/or height field(s) back to their original
     303        # values.
     304        if self.width_field or self.height_field:
     305            if original_object and not change:
     306                if self.width_field:
     307                    setattr(new_object, self.width_field, getattr(original_object, self.width_field))
     308                if self.height_field:
     309                    setattr(new_object, self.height_field, getattr(original_object, self.height_field))
     310            else:
     311                from django.utils.images import get_image_dimensions
     312
     313                upload_field_name = self.get_manipulator_field_names('')[0]
     314                if rel:
     315                    file = new_data[upload_field_name][0]
     316                else:
     317                    file = new_data[upload_field_name]
     318
     319                # Get the width and height from the raw content to avoid extra
     320                # unnecessary trips to the file backend.
     321                width, height = get_image_dimensions(file)
     322
     323                if self.width_field:
     324                    setattr(new_object, self.width_field, width)
     325                if self.height_field:
     326                    setattr(new_object, self.height_field, height)
     327        super(ImageField, self).save_form_data(instance, data)
     328
     329    def formfield(self, **kwargs):
     330        defaults = {'form_class': forms.ImageField}
     331        defaults.update(kwargs)
     332        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

     
    1 """
    2 Utility functions for handling images.
     1import warnings
    32
    4 Requires PIL, as you might imagine.
    5 """
     3from django.core.files.images import get_image_dimensions
    64
    7 import ImageFile
    8 
    9 def get_image_dimensions(path):
    10     """Returns the (width, height) of an image at a given path."""
    11     p = ImageFile.Parser()
    12     fp = open(path, 'rb')
    13     while 1:
    14         data = fp.read(1024)
    15         if not data:
    16             break
    17         p.feed(data)
    18         if p.image:
    19             return p.image.size
    20             break
    21     fp.close()
    22     return None
     5warnings.warn("django.utils.images has been moved to django.core.files.images.", DeprecationWarning)
  • docs/custom_model_fields.txt

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

     
    22982298get_FOO_filename()
    22992299------------------
    23002300
     2301**Deprecated in Django development version. See `managing files`_ for the new,
     2302preferred method for dealing with files.**
     2303
    23012304For every ``FileField``, the object will have a ``get_FOO_filename()`` method,
    23022305where ``FOO`` is the name of the field. This returns the full filesystem path
    23032306to the file, according to your ``MEDIA_ROOT`` setting.
     
    23132316get_FOO_url()
    23142317-------------
    23152318
     2319**Deprecated in Django development version. See `managing files`_ for the new,
     2320preferred method for dealing with files.**
     2321
    23162322For every ``FileField``, the object will have a ``get_FOO_url()`` method,
    23172323where ``FOO`` is the name of the field. This returns the full URL to the file,
    23182324according to your ``MEDIA_URL`` setting. If the value is blank, this method
     
    23262332get_FOO_size()
    23272333--------------
    23282334
     2335**Deprecated in Django development version. See `managing files`_ for the new,
     2336preferred method for dealing with files.**
     2337
    23292338For every ``FileField``, the object will have a ``get_FOO_size()`` method,
    23302339where ``FOO`` is the name of the field. This returns the size of the file, in
    23312340bytes. (Behind the scenes, it uses ``os.path.getsize``.)
     
    23332342save_FOO_file(filename, raw_contents)
    23342343-------------------------------------
    23352344
     2345**Deprecated in Django development version. See `managing files`_ for the new,
     2346preferred method for dealing with files.**
     2347
    23362348For every ``FileField``, the object will have a ``save_FOO_file()`` method,
    23372349where ``FOO`` is the name of the field. This saves the given file to the
    23382350filesystem, using the given filename. If a file with the given filename already
     
    23422354get_FOO_height() and get_FOO_width()
    23432355------------------------------------
    23442356
     2357**Deprecated in Django development version. See `managing files`_ for the new,
     2358preferred method for dealing with files.**
     2359
    23452360For every ``ImageField``, the object will have ``get_FOO_height()`` and
    23462361``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
    23472362returns the height (or width) of the image, as an integer, in pixels.
    23482363
     2364.. _`managing files`: ../files/
     2365
    23492366Shortcuts
    23502367=========
    23512368
  • 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
     13The ``File`` object
     14===================
     15
     16Any time Django references a file, it will be an instance of the `File` class,
     17living at `django.core.files`, or one of its subclasses. This provides many of
     18the same features as Python's own `file object`_, but has a few additions and
     19is customized for working with large-scale applications.
     20
     21.. _file object: http://docs.python.org/lib/bltin-file-objects.html
     22
     23Example
     24-------
     25
     26The simplest way to get access to a `File` is to use Django's drop-in
     27replacement for Python's built-in `open()`, located at django.core.files::
     28
     29    >>> from django.core.files import open
     30
     31    >>> writable = open('/tmp/hello_world', 'w')
     32    >>> writable.name, writable.mode
     33    ('/tmp/hello_world', 'w')
     34    >>> writable.write('Hello, world!')
     35    >>> writable.close()
     36
     37    >>> readable = open('/tmp/hello_world')
     38    >>> readable.size
     39    13
     40    >>> readable.close()
     41
     42``path``
     43--------
     44
     45Returns the absolute path to the file's location on a local filesystem. For
     46storage systems which do not store files locally, this will return `None`.
     47
     48``url``
     49-------
     50
     51Provides a URL where the content of the file can be retrieved. Therefore,
     52returned from this method is suitable for use as the destination of a link to
     53the file.
     54
     55``size``
     56--------
     57
     58Returns the size of the file, as an integer.
     59
     60``open(mode='rb')``
     61-------------------
     62
     63Re-opens the file, resetting the internal pointer back to the beginning. The
     64``mode`` argument allows the same values as Python's standard ``open()``, and
     65overrides whatever mode the file was originally opened with.
     66
     67``read(num_bytes=None)``
     68------------------------
     69
     70Retrieves some content from the file, returning it as a string. The optional
     71``size`` is the number of bytes to read; if not specified, the file will be read
     72through to the end.
     73
     74``__iter__()``
     75--------------
     76
     77Iterates over the lines in the file, preserving newline characters on each line.
     78This allows the file to be used in simple loops, using ``for line in file``.
     79
     80``chunks(chunk_size=None)``
     81---------------------------
     82
     83Yields the contents of the file in smaller chunks, that can be looped over to
     84access the file without loading it all into memory at once. If ``chunk_size``
     85isn't provided, it defaults to 64 KB.
     86
     87``multiple_chunks(chunk_size=None)``
     88------------------------------------
     89
     90Returns ``True`` if the file is large enough to require multiple chunks to
     91access all of its content, or ``False`` if it can all be read in one pass. The
     92optional ``chunk_size`` works the same as in ``chunks()`` above.
     93
     94``write(content)``
     95------------------
     96
     97Writes the specified content string to the file. Depending on the storage system
     98behind the scenes, this content might not be fully committed until ``close()``
     99is called on the file.
     100
     101``close()``
     102-----------
     103
     104Closes the file, so it can't be read from or written to anymore. If there's
     105still any content that hasn't been written to the file itself, this will commit
     106that as well.
     107
     108``ImageFile``
     109=============
     110
     111Anywhere Django can open a new file, it also accepts a mixin class to support
     112more specific file types. For images, there's a more specific ``ImageFile``,
     113available from ``django.core.files.images``.
     114
     115``width and height``
     116--------------------
     117
     118When using an ``ImageField``, these two attributes will be available, providing
     119easy access to the dimensions of the image.
     120
     121Using files in models
     122=====================
     123
     124When accessing a ``FileField`` attached to a model, a special object provides
     125access to the file and information about it.
     126
     127Example
     128-------
     129
     130Consider the following model, using an ``ImageField`` to store a product photo::
     131
     132    class Product(models.Model):
     133        name = models.CharField(maxlength=255)
     134        price = models.DecimalField(max_digits=5, decimal_places=2)
     135        photo = models.ImageField(upload_to='product_photos')
     136
     137Your views can then use the ``photo`` attribute with the functions described
     138above, as follows::
     139
     140    >>> car = Product.object.get(name="'57 Chevy")
     141    >>> car.photo
     142    <ImageFieldFile: 123.jpg>
     143    >>> car.photo.url
     144    '/products/photo/123.jpg'
     145    >>> car.photo.width, car.photo.height
     146    (800, 600)
     147
     148``save(name, content, save=True)``
     149----------------------------------
     150
     151Saves a new file with the filename and contents provided. This will not replace
     152the existing file, but will create a new file and update the object to point to
     153it. The optional ``save`` argument dictates whether the model instance will be
     154saved to the database immediately.
     155
     156``delete(save=True)``
     157---------------------
     158
     159Removes the file from the model instance and deletes it from the underlying
     160storage system. The optional ``save`` argument indicates whether the model
     161instance will saved to the database immediately.
     162
     163Using a storage system with FileField
     164=====================================
     165
     166When using a storage system, supply whatever options are appropriate for
     167that system when creating a new object. Then pass that object as the ``storage``
     168argument to a ``FileField``. Details on the requirements for the included
     169storage system can be found below.
     170
     171If using the default storage system, it is not necessary to create a storage
     172object explicitly. In this case, the ``FileField`` will use the one referenced
     173by the `DEFAULT_FILE_STORAGE setting`_.
     174
     175See the `FileField documentation`_ for more information on using the field.
     176
     177.. _DEFAULT_FILE_STORAGE setting: ../settings/#default-file-storage
     178.. _FileField documentation: ../model-api/#filefield
     179
     180For example, the following code will explicitly use the ``FileSystemStorage``::
     181
     182    from django.db import models
     183    from django.core.files.storage import FileSystemStorage
     184   
     185    fs = FileSystemStorage(location='product_photos')
     186   
     187    class Product(models.Model):
     188        name = models.CharField(maxlength=255)
     189        price = models.DecimalField(max_digits=5, decimal_places=2)
     190        photo = models.ImageField(storage=fs)
     191
     192Using a storage system on its own
     193=================================
     194
     195Storage systems may also be used directly, without being attached to a model.
     196Simply use the following API on any instantiated storage system to access files
     197without having to worry about the underlying mechanism. In addition to explicit
     198storage mechanisms, the file storage module, ``django.core.files.storage``,
     199exports a ``default_storage`` object that's automatically created from the
     200``DEFAULT_FILE_STORAGE`` setting::
     201
     202    >>> from django.core.files.storage import default_storage
     203
     204With a functional storage system on hand, managing files is quite simple, with a
     205few basic methods to handle the most common operations::
     206
     207    >>> path = storage.save('/path/to/file', 'new content')
     208    >>> path
     209    u'/path/to/file'
     210    >>> storage.filesize(path)
     211    11
     212    >>> storage.open(path).read()
     213    'new content'
     214    >>> storage.delete(path)
     215    >>> storage.exists(path)
     216    False
     217
     218``exists(name)``
     219----------------
     220
     221Returns ``True`` or ``False, indicating whether there is already a file present
     222at the location referenced by``name``.
     223
     224``path(name)``
     225--------------
     226
     227Returns the local filesystem path where the file can be opened using Python's
     228standard ``open()``. For storage systems that aren't accessible from the local
     229filesystem, this will raise ``NotImplementedError`` instead.
     230
     231``size(name)``
     232--------------
     233
     234Returns the total size, in bytes, of the file referenced by ``name``.
     235
     236``url(name)``
     237-------------
     238
     239Returns the URL where the contents of the file referenced by ``name`` can be
     240accessed.
     241
     242``open(name, mode='rb', mixin=None)``
     243-------------------------------------
     244
     245Returns an open file, or file-like, object to provide access to the contents of
     246the file referenced by ``name``. The ``mode`` argument allows the same values as
     247Python's standard ``open()`` function. The ``mixin`` is an optional class that,
     248if provided, will be applied to the ``File`` object returned from this method.
     249
     250``save(name, content)``
     251-----------------------
     252
     253Saves a new file using the storage system, preferably with the name specified.
     254If there already exists a file at the location referenced by ``name``, this may
     255modify the filename as necessary to locate one that is available. Once the file
     256is saved, this method will return the filename where the file was actually
     257stored.
     258
     259``delete(name)``
     260----------------
     261
     262Deletes the file referenced by ``name``. If the file does not already exist,
     263this method will simply return without raising an exception.
     264
     265Available storage systems
     266=========================
     267
     268Only one storage system is supplied in the official Django distribution, but
     269more may be available elsewhere. If you'd like to use a different storage system
     270than the one listed below, see the documentation included with it.
     271
     272``django.core.files.storage.FileSystemStorage``
     273-----------------------------------------------
     274
     275This simply stores files on the system's standard filesystem.
     276
     277    ======================  ===================================================
     278    Argument                Description
     279    ======================  ===================================================
     280    ``location``            Optional. Absolute path to the directory that will
     281                            hold the files. If omitted, it will be set to the
     282                            value of your ``MEDIA_ROOT`` setting.
     283    ``base_url``            Optional. URL that serves the files stored at this
     284                            location. If omitted, it will default to the value
     285                            of your ``MEDIA_URL`` setting.
     286    ======================  ===================================================
     287
     288Writing a storage system
     289========================
     290
     291While the default filesystem storage is suitable for most needs, there are many
     292other storage mechanisms that may be used, and situations that will require
     293special processing. In order to use Django in these environments, it's fairly
     294simple to write a new storage system, creating a wrapper around whatever
     295libraries are used to access your files, or simply customizing method calls on
     296an existing storage class.
     297
     298If a storage system requires any configuration options to determine how it
     299should access the underlying storage mechanism or cusotmize its behavior in
     300other ways, those options should be specified in a particular way. Because the
     301default storage system is specified as a string, Django must be able to
     302instantiate it without any arguments, and any required arguments should be
     303specified as global settings, which can be referenced from the storage system.
     304For example::
     305
     306    from django.conf import settings
     307    from django.core.files.storage import Storage
     308
     309    class CustomStorage(Storage):
     310        def __init__(self, option=settings.CUSTOM_STORAGE_OPTION):
     311            ...
     312
     313Every storage system will have all the methods described above, but there are a
     314few with have default behaviors that shouldn't be overridden by these subclasses
     315in most situations. Each of these has a different set of responsibilities that
     316the storage system is expected to fulfill:
     317
     318 * ``path()`` -- unless the class provides access to files that are also
     319   accessible via the local filesystem, this should inherit the default behavior
     320   of raising a ``NotImplementedError``. For those that do represent portions of
     321   the filesystem, subclassing ``FileSystemStorage`` will typically be more
     322   appropriate anyway.
     323   
     324 * ``open()`` -- This provides some additional logic    that isn't specific to
     325   file retrieval, by supporting the ``mixin`` argument. Instead of overriding
     326   this directly, storage systems should provide an ``_open()`` method as
     327   described below.
     328   
     329 * ``save()`` -- The ``name`` provided to this is actually more a preference,
     330   because it will actually go through both ``get_valid_name()`` and
     331   ``get_available_name()`` to determine what name the will actually be given.
     332   It also returns the final name, taking care to adjust it to Unix-style paths.
     333   Since these features aren't related to actually storing the file, subclasses
     334   should instead provide a ``_save()`` method as described below.
     335
     336The default beaviors for these methods are provided by the provided ``Storage``
     337class, living at ``django.files.storage``. In addition, the two other methods
     338used by ``save()`` internally to determine the final filename, which have
     339default implementations, but can be overridden, and there are two other methods
     340that must be provided for all storage systems.
     341
     342``get_valid_name(name)``
     343------------------------
     344
     345Returns a filename suitable for use with the underlying storage system. The
     346``name`` argument passed to this method is the original filename sent to the
     347server, after having any path information removed. Override this to customize
     348how non-standard characters are converted to safe filenames.
     349
     350The code provided on ``Storage`` retains only alpha-numeric characters, periods
     351and underscores from the original filename, removing everything else.
     352
     353``get_available_name(name)``
     354----------------------------
     355
     356Returns a filename that is available in the storage mechanism, possibly taking
     357the provided filename into account. The ``name`` argument passed to this method
     358will have already cleaned to a filename valid for the storage system, according
     359to the ``get_valid_name()`` method described above.
     360
     361The code provided on ``Storage`` simply appends underscores to the filename
     362until it finds one that's available in the destination directory.
     363
     364``_open(name, mode='rb')``
     365--------------------------
     366
     367Returns an open ``File`` object that can be used to access the file's contents.
     368The ``mode`` represents all the same values as Python's own ``open()``, and
     369should be used to determine how the file can be accessed. See below for details
     370regarding how the returned ``File`` object should be behave for reading and
     371writing content.
     372
     373``_save(name, content)``
     374------------------------
     375
     376Stores the given content to the persistent storage backed by the class. The
     377``name`` will already have gone through ``get_valid_name()`` and
     378``get_available_name()``, and the ``content`` will be a ``File`` object itself.
     379This method has no return value.
     380
     381Providing a ``File``
     382--------------------
     383
     384Since the ``open()`` method returns a ``File`` object, it's expected that a
     385``Storage`` subclass will provide a customized version that's designed to
     386interact with the underlying storage system. Many methods, such as ``read()``,
     387``write()`` and ``close()``, should be overridden on this new ``File`` subclass,
     388so that it can transparently access the file's contents.
  • docs/model-api.txt

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

     
    426426isn't manually specified. Used with ``DEFAULT_CHARSET`` to construct the
    427427``Content-Type`` header.
    428428
     429DEFAULT_FILE_STORAGE
     430--------------------
     431
     432Default: ``'django.core.filestorage.filesystem.FileSystemStorage'``
     433
     434Default file storage class to be used for any file-related operations that don't
     435specify a particular storage system. See the `file documentation`_ for details.
     436
     437.. _file documentation: ../files/
     438
    429439DEFAULT_FROM_EMAIL
    430440------------------
    431441
  • docs/upload_handling.txt

     
    155155``UploadedFile`` objects
    156156========================
    157157
    158 All ``UploadedFile`` objects define the following methods/attributes:
     158In addition to those inherited from `File`_, all ``UploadedFile`` objects define
     159the following methods/attributes:
    159160
    160     ``UploadedFile.read(self, num_bytes=None)``
    161         Returns a byte string of length ``num_bytes``, or the complete file if
    162         ``num_bytes`` is ``None``.
    163 
    164     ``UploadedFile.chunks(self, chunk_size=None)``
    165         A generator yielding small chunks from the file. If ``chunk_size`` isn't
    166         given, chunks will be 64 KB.
    167 
    168     ``UploadedFile.multiple_chunks(self, chunk_size=None)``
    169         Returns ``True`` if you can expect more than one chunk when calling
    170         ``UploadedFile.chunks(self, chunk_size)``.
    171 
    172     ``UploadedFile.size``
    173         The size, in bytes, of the uploaded file.
    174 
    175     ``UploadedFile.name``
    176         The name of the uploaded file as provided by the user.
    177 
    178161    ``UploadedFile.content_type``
    179162        The content-type header uploaded with the file (e.g. ``text/plain`` or
    180163        ``application/pdf``). Like any data supplied by the user, you shouldn't
     
    186169        For ``text/*`` content-types, the character set (i.e. ``utf8``) supplied
    187170        by the browser. Again, "trust but verify" is the best policy here.
    188171
    189     ``UploadedFile.__iter__()``
    190         Iterates over the lines in the file.
    191 
    192172    ``UploadedFile.temporary_file_path()``
    193173        Only files uploaded onto disk will have this method; it returns the full
    194174        path to the temporary uploaded file.
    195175
     176.. _File: ../files/
    196177
    197178Upload Handlers
    198179===============
  • tests/modeltests/files/__init__.py

     
     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.files.base import ContentFile
     12from django.core.files.storage import FileSystemStorage
     13from django.core.cache import cache
     14
     15temp_storage = FileSystemStorage(location=tempfile.gettempdir())
     16
     17# Write out a file to be used as default content
     18temp_storage.save('tests/default.txt', ContentFile('default content'))
     19
     20class Storage(models.Model):
     21    def custom_upload_to(self, filename):
     22        return 'foo'
     23
     24    def random_upload_to(self, filename):
     25        # This returns a different result each time,
     26        # to make sure it only gets called once.
     27        import random
     28        return '%s/%s' % (random.randint(100, 999), filename)
     29
     30    normal = models.FileField(storage=temp_storage, upload_to='tests')
     31    custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to)
     32    random = models.FileField(storage=temp_storage, upload_to=random_upload_to)
     33    default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt')
     34
     35__test__ = {'API_TESTS':"""
     36# An object without a file has limited functionality.
     37
     38>>> obj1 = Storage()
     39>>> obj1.normal
     40<FieldFile: None>
     41>>> obj1.normal.size
     42Traceback (most recent call last):
     43...
     44ValueError: The 'normal' attribute has no file associated with it.
     45
     46# Saving a file enables full functionality.
     47
     48>>> obj1.normal.save('django_test.txt', ContentFile('content'))
     49>>> obj1.normal
     50<FieldFile: tests/django_test.txt>
     51>>> obj1.normal.size
     527
     53>>> obj1.normal.read()
     54'content'
     55
     56# Files can be read in a little at a time, if necessary.
     57
     58>>> obj1.normal.open()
     59>>> obj1.normal.read(3)
     60'con'
     61>>> obj1.normal.read()
     62'tent'
     63>>> '-'.join(obj1.normal.chunks(chunk_size=2))
     64'co-nt-en-t'
     65
     66# Save another file with the same name.
     67
     68>>> obj2 = Storage()
     69>>> obj2.normal.save('django_test.txt', ContentFile('more content'))
     70>>> obj2.normal
     71<FieldFile: tests/django_test_.txt>
     72>>> obj2.normal.size
     7312
     74
     75# Push the objects into the cache to make sure they pickle properly
     76
     77>>> cache.set('obj1', obj1)
     78>>> cache.set('obj2', obj2)
     79>>> cache.get('obj2').normal
     80<FieldFile: tests/django_test_.txt>
     81
     82# Deleting an object deletes the file it uses, if there are no other objects
     83# still using that file.
     84
     85>>> obj2.delete()
     86>>> obj2.normal.save('django_test.txt', ContentFile('more content'))
     87>>> obj2.normal
     88<FieldFile: tests/django_test_.txt>
     89
     90# Default values allow an object to access a single file.
     91
     92>>> obj3 = Storage.objects.create()
     93>>> obj3.default
     94<FieldFile: tests/default.txt>
     95>>> obj3.default.read()
     96'default content'
     97
     98# But it shouldn't be deleted, even if there are no more objects using it.
     99
     100>>> obj3.delete()
     101>>> obj3 = Storage()
     102>>> obj3.default.read()
     103'default content'
     104
     105# Verify the fix for #5655, making sure the directory is only determined once.
     106
     107>>> obj4 = Storage()
     108>>> obj4.random.save('random_file', ContentFile('random content'))
     109>>> obj4.random
     110<FieldFile: .../random_file>
     111
     112# Clean up the temporary files.
     113
     114>>> obj1.normal.delete()
     115>>> obj2.normal.delete()
     116>>> obj3.default.delete()
     117>>> obj4.random.delete()
     118"""}
  • tests/modeltests/model_forms/models.py

     
    1111import tempfile
    1212
    1313from django.db import models
     14from django.core.files.storage 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
     
    7376        # for PyPy, you need to check for the underlying modules
    7477        # If PIL is not available, this test is equivalent to TextFile above.
    7578        import Image, _imaging
    76         image = models.ImageField(upload_to=tempfile.gettempdir())
     79        image = models.ImageField(storage=temp_storage, upload_to='tests')
    7780    except ImportError:
    78         image = models.FileField(upload_to=tempfile.gettempdir())
     81        image = models.FileField(storage=temp_storage, upload_to='tests')
    7982
    8083    def __unicode__(self):
    8184        return self.description
     
    786789
    787790# FileField ###################################################################
    788791
     792# File forms.
     793
    789794>>> class TextFileForm(ModelForm):
    790795...     class Meta:
    791796...         model = TextFile
     
    808813<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
    809814>>> instance = f.save()
    810815>>> instance.file
    811 u'...test1.txt'
     816<FieldFile: tests/test1.txt>
    812817
    813 >>> os.unlink(instance.get_file_filename())
     818>>> instance.file.delete()
    814819
    815820>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')})
    816821>>> f.is_valid()
     
    819824<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
    820825>>> instance = f.save()
    821826>>> instance.file
    822 u'...test1.txt'
     827<FieldFile: tests/test1.txt>
    823828
    824829# Edit an instance that already has the file defined in the model. This will not
    825830# save the file again, but leave it exactly as it is.
     
    828833>>> f.is_valid()
    829834True
    830835>>> f.cleaned_data['file']
    831 u'...test1.txt'
     836<FieldFile: tests/test1.txt>
    832837>>> instance = f.save()
    833838>>> instance.file
    834 u'...test1.txt'
     839<FieldFile: tests/test1.txt>
    835840
    836841# Delete the current file since this is not done by Django.
    837 >>> os.unlink(instance.get_file_filename())
     842>>> instance.file.delete()
    838843
    839844# Override the file by uploading a new one.
    840845
     
    843848True
    844849>>> instance = f.save()
    845850>>> instance.file
    846 u'...test2.txt'
     851<FieldFile: tests/test2.txt>
    847852
    848853# Delete the current file since this is not done by Django.
    849 >>> os.unlink(instance.get_file_filename())
     854>>> instance.file.delete()
    850855
    851856>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')})
    852857>>> f.is_valid()
    853858True
    854859>>> instance = f.save()
    855860>>> instance.file
    856 u'...test2.txt'
     861<FieldFile: tests/test2.txt>
    857862
    858863# Delete the current file since this is not done by Django.
    859 >>> os.unlink(instance.get_file_filename())
     864>>> instance.file.delete()
    860865
    861866>>> instance.delete()
    862867
     
    868873True
    869874>>> instance = f.save()
    870875>>> instance.file
    871 ''
     876<FieldFile: None>
    872877
    873878>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance)
    874879>>> f.is_valid()
    875880True
    876881>>> instance = f.save()
    877882>>> instance.file
    878 u'...test3.txt'
     883<FieldFile: tests/test3.txt>
    879884
    880885# Delete the current file since this is not done by Django.
    881 >>> os.unlink(instance.get_file_filename())
     886>>> instance.file.delete()
    882887>>> instance.delete()
    883888
    884889>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')})
     
    886891True
    887892>>> instance = f.save()
    888893>>> instance.file
    889 u'...test3.txt'
     894<FieldFile: tests/test3.txt>
    890895
    891896# Delete the current file since this is not done by Django.
    892 >>> os.unlink(instance.get_file_filename())
     897>>> instance.file.delete()
    893898>>> instance.delete()
    894899
    895900# ImageField ###################################################################
     
    911916<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
    912917>>> instance = f.save()
    913918>>> instance.image
    914 u'...test.png'
     919<ImageFieldFile: tests/test.png>
    915920
    916921# Delete the current file since this is not done by Django.
    917 >>> os.unlink(instance.get_image_filename())
     922>>> instance.image.delete()
    918923
    919924>>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
    920925>>> f.is_valid()
     
    923928<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
    924929>>> instance = f.save()
    925930>>> instance.image
    926 u'...test.png'
     931<ImageFieldFile: tests/test.png>
    927932
    928933# Edit an instance that already has the image defined in the model. This will not
    929934# save the image again, but leave it exactly as it is.
     
    932937>>> f.is_valid()
    933938True
    934939>>> f.cleaned_data['image']
    935 u'...test.png'
     940<ImageFieldFile: tests/test.png>
    936941>>> instance = f.save()
    937942>>> instance.image
    938 u'...test.png'
     943<ImageFieldFile: tests/test.png>
    939944
    940945# Delete the current image since this is not done by Django.
    941946
    942 >>> os.unlink(instance.get_image_filename())
     947>>> instance.image.delete()
    943948
    944949# Override the file by uploading a new one.
    945950
     
    948953True
    949954>>> instance = f.save()
    950955>>> instance.image
    951 u'...test2.png'
     956<ImageFieldFile: tests/test2.png>
    952957
    953958# Delete the current file since this is not done by Django.
    954 >>> os.unlink(instance.get_image_filename())
     959>>> instance.image.delete()
    955960>>> instance.delete()
    956961
    957962>>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)})
     
    959964True
    960965>>> instance = f.save()
    961966>>> instance.image
    962 u'...test2.png'
     967<ImageFieldFile: tests/test2.png>
    963968
    964969# Delete the current file since this is not done by Django.
    965 >>> os.unlink(instance.get_image_filename())
     970>>> instance.image.delete()
    966971>>> instance.delete()
    967972
    968973# Test the non-required ImageField
     
    973978True
    974979>>> instance = f.save()
    975980>>> instance.image
    976 ''
     981<ImageFieldFile: None>
    977982
    978983>>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance)
    979984>>> f.is_valid()
    980985True
    981986>>> instance = f.save()
    982987>>> instance.image
    983 u'...test3.png'
     988<ImageFieldFile: tests/test3.png>
    984989
    985990# Delete the current file since this is not done by Django.
    986 >>> os.unlink(instance.get_image_filename())
     991>>> instance.image.delete()
    987992>>> instance.delete()
    988993
    989994>>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)})
     
    991996True
    992997>>> instance = f.save()
    993998>>> instance.image
    994 u'...test3.png'
     999<ImageFieldFile: tests/test3.png>
    9951000>>> instance.delete()
    9961001
    9971002# Media on a ModelForm ########################################################
  • tests/regressiontests/bug639/models.py

     
    11import tempfile
     2
    23from django.db import models
     4from django.core.files.storage 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
  • tests/regressiontests/bug639/tests.py

     
    3636        Make sure to delete the "uploaded" file to avoid clogging /tmp.
    3737        """
    3838        p = Photo.objects.get()
    39         os.unlink(p.get_image_filename())
     39        p.image.delete(save=False)
  • tests/regressiontests/file_uploads/models.py

     
    11import tempfile
    22import os
    33from django.db import models
     4from django.core.files.storage import FileSystemStorage
    45
    5 UPLOAD_ROOT = tempfile.mkdtemp()
    6 UPLOAD_TO = os.path.join(UPLOAD_ROOT, 'test_upload')
     6temp_storage = FileSystemStorage(tempfile.mkdtemp())
     7UPLOAD_TO = os.path.join(temp_storage.location, 'test_upload')
    78
    89class FileModel(models.Model):
    9     testfile = models.FileField(upload_to=UPLOAD_TO)
     10    testfile = models.FileField(storage=temp_storage, upload_to='test_upload')
  • tests/regressiontests/file_uploads/tests.py

     
    99from django.test import TestCase, client
    1010from django.utils import simplejson
    1111
    12 from models import FileModel, UPLOAD_ROOT, UPLOAD_TO
     12from models import FileModel, temp_storage, UPLOAD_TO
    1313
    1414class FileUploadTests(TestCase):
    1515    def test_simple_upload(self):
     
    194194    """
    195195    def setUp(self):
    196196        self.obj = FileModel()
    197         if not os.path.isdir(UPLOAD_ROOT):
    198             os.makedirs(UPLOAD_ROOT)
     197        if not os.path.isdir(temp_storage.location):
     198            os.makedirs(temp_storage.location)
    199199
    200200    def tearDown(self):
    201         os.chmod(UPLOAD_ROOT, 0700)
    202         shutil.rmtree(UPLOAD_ROOT)
     201        os.chmod(temp_storage.location, 0700)
     202        shutil.rmtree(temp_storage.location)
    203203
    204204    def test_readonly_root(self):
    205205        """Permission errors are not swallowed"""
    206         os.chmod(UPLOAD_ROOT, 0500)
     206        os.chmod(temp_storage.location, 0500)
    207207        try:
    208             self.obj.save_testfile_file('foo.txt', SimpleUploadedFile('foo.txt', 'x'))
     208            self.obj.testfile.save('foo.txt', SimpleUploadedFile('foo.txt', 'x'))
    209209        except OSError, err:
    210210            self.assertEquals(err.errno, errno.EACCES)
    211         except:
    212             self.fail("OSError [Errno %s] not raised" % errno.EACCES)
     211        except Exception, err:
     212            self.fail("OSError [Errno %s] not raised." % errno.EACCES)
    213213
    214214    def test_not_a_directory(self):
    215215        """The correct IOError is raised when the upload directory name exists but isn't a directory"""
     
    217217        fd = open(UPLOAD_TO, 'w')
    218218        fd.close()
    219219        try:
    220             self.obj.save_testfile_file('foo.txt', SimpleUploadedFile('foo.txt', 'x'))
     220            self.obj.testfile.save('foo.txt', SimpleUploadedFile('foo.txt', 'x'))
    221221        except IOError, err:
    222222            # The test needs to be done on a specific string as IOError
    223223            # is raised even without the patch (just not early enough)
    224224            self.assertEquals(err.args[0],
    225                               "%s exists and is not a directory" % UPLOAD_TO)
     225                              "%s exists and is not a directory." % UPLOAD_TO)
    226226        except:
    227227            self.fail("IOError not raised")
  • tests/regressiontests/forms/models.py

     
    2929>>> from django.core.files.uploadedfile import SimpleUploadedFile
    3030
    3131# FileModel with unicode filename and data #########################
    32 >>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('我隻氣墊船裝滿晒鱔.txt', 'मेरी मँडराने वाली नाव सर्पमीनों से भरी ह')}, auto_id=False)
     32>>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('?????????.txt', '???? ??????? ???? ??? ????????? ?? ??? ?')}, auto_id=False)
    3333>>> f.is_valid()
    3434True
    3535>>> f.cleaned_data
    36 {'file1': <SimpleUploadedFile: 我隻氣墊船裝滿晒鱔.txt (text/plain)>}
     36{'file1': <SimpleUploadedFile: ?????????.txt (text/plain)>}
    3737>>> m = FileModel.objects.create(file=f.cleaned_data['file1'])
    3838
    3939# Boundary conditions on a PostitiveIntegerField #########################
  • 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

     
    144144    (data_obj, 41, EmailData, None),
    145145    (data_obj, 42, EmailData, ""),
    146146    (data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'),
    147     (data_obj, 51, FileData, None),
     147#     (data_obj, 51, FileData, None),
    148148    (data_obj, 52, FileData, ""),
    149149    (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"),
    150150    (data_obj, 61, FilePathData, None),
     
    242242#     (pk_obj, 620, DatePKData, datetime.date(2006,6,16)),
    243243#     (pk_obj, 630, DateTimePKData, datetime.datetime(2006,6,16,10,42,37)),
    244244    (pk_obj, 640, EmailPKData, "hovercraft@example.com"),
    245     (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
     245#     (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
    246246    (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"),
    247247    (pk_obj, 670, DecimalPKData, decimal.Decimal('12.345')),
    248248    (pk_obj, 671, DecimalPKData, decimal.Decimal('-12.345')),
  • tests/regressiontests/storage/__init__.py

     
     1
  • tests/regressiontests/storage/models.py

     
     1# Empty file to force tests to run
  • tests/regressiontests/storage/tests.py

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