Ticket #5361: filestorage.10.diff

File filestorage.10.diff, 64.1 KB (added by Marty Alchin, 11 years ago)

Fixed merge problems, updated to r7091, passed all tests, and removed a few uses of the now-deprecated get_FOO_*() methods

  • django/core/filestorage/base.py

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

     
     1import os
     2import urlparse
     3
     4from django.conf import settings
     5from django.utils.encoding import force_unicode, smart_str
     6from django.core.filestorage.base import Backend
     7from django.utils.text import force_unicode
     8
     9class FileSystemBackend(Backend):
     10    """Standard filesystem storage"""
     11
     12    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
     13        self.location = os.path.abspath(location)
     14        self.base_url = base_url
     15
     16    def get_absolute_path(self, filename):
     17        return os.path.normpath(os.path.join(self.location, filename))
     18
     19    def get_filesize(self, filename):
     20        return os.path.getsize(self.get_absolute_path(filename))
     21
     22    def get_absolute_url(self, filename):
     23        return urlparse.urljoin(self.base_url, filename).replace('\\', '/')
     24
     25    def open_file(self, filename, mode='rb'):
     26        return open(self.get_absolute_path(filename), mode)
     27
     28    def file_exists(self, filename):
     29        return os.path.exists(self.get_absolute_path(filename))
     30
     31    def save_file(self, filename, raw_contents):
     32        try: # Create the destination directory if it doesn't exist.
     33            os.makedirs(os.path.join(self.location, os.path.dirname(filename)))
     34        except OSError: # Directory probably already exists.
     35            pass
     36        filename = self.get_available_filename(filename)
     37
     38        # Write the file to disk.
     39        fp = self.open_file(filename, 'wb')
     40        fp.write(raw_contents)
     41        fp.close()
     42
     43        # Store filenames with forward slashes, even on Windows
     44        return force_unicode(filename.replace('\\', '/'))
     45
     46    def delete_file(self, filename):
     47        file_name = self.get_absolute_path(filename)
     48        # If the file exists, delete it from the filesystem.
     49        if os.path.exists(file_name):
     50            os.remove(file_name)
  • django/core/filestorage/base.py

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

     
     1import os
     2import urlparse
     3
     4from django.conf import settings
     5from django.utils.encoding import force_unicode, smart_str
     6from django.core.filestorage.base import Backend
     7from django.utils.text import force_unicode
     8
     9class FileSystemBackend(Backend):
     10    """Standard filesystem storage"""
     11
     12    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
     13        self.location = os.path.abspath(location)
     14        self.base_url = base_url
     15
     16    def get_absolute_path(self, filename):
     17        return os.path.normpath(os.path.join(self.location, filename))
     18
     19    def get_filesize(self, filename):
     20        return os.path.getsize(self.get_absolute_path(filename))
     21
     22    def get_absolute_url(self, filename):
     23        return urlparse.urljoin(self.base_url, filename).replace('\\', '/')
     24
     25    def open_file(self, filename, mode='rb'):
     26        return open(self.get_absolute_path(filename), mode)
     27
     28    def file_exists(self, filename):
     29        return os.path.exists(self.get_absolute_path(filename))
     30
     31    def save_file(self, filename, raw_contents):
     32        try: # Create the destination directory if it doesn't exist.
     33            os.makedirs(os.path.join(self.location, os.path.dirname(filename)))
     34        except OSError: # Directory probably already exists.
     35            pass
     36        filename = self.get_available_filename(filename)
     37
     38        # Write the file to disk.
     39        fp = self.open_file(filename, 'wb')
     40        fp.write(raw_contents)
     41        fp.close()
     42
     43        # Store filenames with forward slashes, even on Windows
     44        return force_unicode(filename.replace('\\', '/'))
     45
     46    def delete_file(self, filename):
     47        file_name = self.get_absolute_path(filename)
     48        # If the file exists, delete it from the filesystem.
     49        if os.path.exists(file_name):
     50            os.remove(file_name)
  • django/core/serializers/base.py

     
    6161        if isinstance(field, models.DateTimeField):
    6262            value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
    6363        elif isinstance(field, models.FileField):
    64             value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
     64            try:
     65                value = getattr(obj, field.name).get_absolute_url()
     66            except ValueError:
     67                value = ""
    6568        else:
    6669            value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
    6770        return smart_unicode(value)
  • django/db/models/__init__.py

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

     
    22import django.db.models.manager
    33from django.core import validators
    44from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
    5 from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
     5from django.db.models.fields import AutoField, FieldDoesNotExist
    66from django.db.models.fields.related import OneToOneRel, ManyToOneRel
    77from django.db.models.query import delete_objects
    88from django.db.models.options import Options, AdminOptions
     
    1818import types
    1919import sys
    2020import os
     21from warnings import warn
    2122
    2223class ModelBase(type):
    2324    "Metaclass for all models"
     
    369370        return getattr(self, cachename)
    370371
    371372    def _get_FIELD_filename(self, field):
    372         if getattr(self, field.attname): # value is not blank
    373             return os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname))
    374         return ''
     373        warn("Use instance.%s.get_absolute_path()." % field.attname, DeprecationWarning)
     374        try:
     375            return getattr(self, field.attname).get_absolute_path()
     376        except ValueError:
     377            # For backward compatibility
     378            return settings.MEDIA_ROOT
    375379
    376380    def _get_FIELD_url(self, field):
    377         if getattr(self, field.attname): # value is not blank
    378             import urlparse
    379             return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
    380         return ''
     381        warn("Use instance.%s.get_absolute_url()." % field.attname, DeprecationWarning)
     382        try:
     383            return getattr(self, field.attname).get_absolute_url()
     384        except ValueError:
     385            # For backward compatibility
     386            return settings.MEDIA_URL
    381387
    382388    def _get_FIELD_size(self, field):
    383         return os.path.getsize(self._get_FIELD_filename(field))
     389        warn("Use instance.%s.get_filesize()." % field.attname, DeprecationWarning)
     390        return getattr(self, field.attname).get_filesize()
    384391
    385392    def _save_FIELD_file(self, field, filename, raw_contents, save=True):
    386         directory = field.get_directory_name()
    387         try: # Create the date-based directory if it doesn't exist.
    388             os.makedirs(os.path.join(settings.MEDIA_ROOT, directory))
    389         except OSError: # Directory probably already exists.
    390             pass
    391         filename = field.get_filename(filename)
     393        warn("Use instance.%s.save_file()." % field.attname, DeprecationWarning)
     394        return getattr(self, field.attname).save_file(filename, raw_contents, save)
    392395
    393         # If the filename already exists, keep adding an underscore to the name of
    394         # the file until the filename doesn't exist.
    395         while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
    396             try:
    397                 dot_index = filename.rindex('.')
    398             except ValueError: # filename has no dot
    399                 filename += '_'
    400             else:
    401                 filename = filename[:dot_index] + '_' + filename[dot_index:]
    402 
    403         # Write the file to disk.
    404         setattr(self, field.attname, filename)
    405 
    406         full_filename = self._get_FIELD_filename(field)
    407         fp = open(full_filename, 'wb')
    408         fp.write(raw_contents)
    409         fp.close()
    410 
    411         # Save the width and/or height, if applicable.
    412         if isinstance(field, ImageField) and (field.width_field or field.height_field):
    413             from django.utils.images import get_image_dimensions
    414             width, height = get_image_dimensions(full_filename)
    415             if field.width_field:
    416                 setattr(self, field.width_field, width)
    417             if field.height_field:
    418                 setattr(self, field.height_field, height)
    419 
    420         # Save the object because it has changed unless save is False
    421         if save:
    422             self.save()
    423 
    424     _save_FIELD_file.alters_data = True
    425 
    426396    def _get_FIELD_width(self, field):
    427         return self._get_image_dimensions(field)[0]
     397        warn("Use instance.%s.get_width()." % field.attname, DeprecationWarning)
     398        return getattr(self, field.attname).get_width()
    428399
    429400    def _get_FIELD_height(self, field):
    430         return self._get_image_dimensions(field)[1]
     401        warn("Use instance.%s.get_height()." % field.attname, DeprecationWarning)
     402        return getattr(self, field.attname).get_height()
    431403
    432     def _get_image_dimensions(self, field):
    433         cachename = "__%s_dimensions_cache" % field.name
    434         if not hasattr(self, cachename):
    435             from django.utils.images import get_image_dimensions
    436             filename = self._get_FIELD_filename(field)
    437             setattr(self, cachename, get_image_dimensions(filename))
    438         return getattr(self, cachename)
    439 
    440404############################################
    441405# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
    442406############################################
  • django/db/models/fields/__init__.py

     
    11import datetime
    2 import os
    32import time
    43try:
    54    import decimal
     
    276275        name_prefix is a prefix to prepend to the "field_name" argument.
    277276        rel is a boolean specifying whether this field is in a related context.
    278277        """
     278        from django.db.models.fields import files
     279
    279280        field_objs, params = self.prepare_field_objs_and_params(manipulator, name_prefix)
    280281
    281282        # Add the "unique" validator(s).
     
    307308        # If this field is in a related context, check whether any other fields
    308309        # in the related object have core=True. If so, add a validator --
    309310        # RequiredIfOtherFieldsGiven -- to this FormField.
    310         if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
     311        if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, files.FileField):
    311312            # First, get the core fields, if any.
    312313            core_field_names = []
    313314            for f in opts.fields:
     
    707708        defaults.update(kwargs)
    708709        return super(EmailField, self).formfield(**defaults)
    709710
    710 class FileField(Field):
    711     def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
    712         self.upload_to = upload_to
    713         kwargs['max_length'] = kwargs.get('max_length', 100)
    714         Field.__init__(self, verbose_name, name, **kwargs)
    715 
    716     def get_db_prep_save(self, value):
    717         "Returns field's value prepared for saving into a database."
    718         # Need to convert UploadedFile objects provided via a form to unicode for database insertion
    719         if value is None:
    720             return None
    721         return unicode(value)
    722 
    723     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
    724         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
    725         if not self.blank:
    726             if rel:
    727                 # This validator makes sure FileFields work in a related context.
    728                 class RequiredFileField(object):
    729                     def __init__(self, other_field_names, other_file_field_name):
    730                         self.other_field_names = other_field_names
    731                         self.other_file_field_name = other_file_field_name
    732                         self.always_test = True
    733                     def __call__(self, field_data, all_data):
    734                         if not all_data.get(self.other_file_field_name, False):
    735                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required."))
    736                             c(field_data, all_data)
    737                 # First, get the core fields, if any.
    738                 core_field_names = []
    739                 for f in opts.fields:
    740                     if f.core and f != self:
    741                         core_field_names.extend(f.get_manipulator_field_names(name_prefix))
    742                 # Now, if there are any, add the validator to this FormField.
    743                 if core_field_names:
    744                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
    745             else:
    746                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required."))
    747                 v.always_test = True
    748                 field_list[0].validator_list.append(v)
    749                 field_list[0].is_required = field_list[1].is_required = False
    750 
    751         # If the raw path is passed in, validate it's under the MEDIA_ROOT.
    752         def isWithinMediaRoot(field_data, all_data):
    753             f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
    754             if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
    755                 raise validators.ValidationError, _("Enter a valid filename.")
    756         field_list[1].validator_list.append(isWithinMediaRoot)
    757         return field_list
    758 
    759     def contribute_to_class(self, cls, name):
    760         super(FileField, self).contribute_to_class(cls, name)
    761         setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
    762         setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
    763         setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
    764         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save))
    765         dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
    766 
    767     def delete_file(self, instance):
    768         if getattr(instance, self.attname):
    769             file_name = getattr(instance, 'get_%s_filename' % self.name)()
    770             # If the file exists and no other object of this type references it,
    771             # delete it from the filesystem.
    772             if os.path.exists(file_name) and \
    773                 not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}):
    774                 os.remove(file_name)
    775 
    776     def get_manipulator_field_objs(self):
    777         return [oldforms.FileUploadField, oldforms.HiddenField]
    778 
    779     def get_manipulator_field_names(self, name_prefix):
    780         return [name_prefix + self.name + '_file', name_prefix + self.name]
    781 
    782     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    783         upload_field_name = self.get_manipulator_field_names('')[0]
    784         if new_data.get(upload_field_name, False):
    785             func = getattr(new_object, 'save_%s_file' % self.name)
    786             if rel:
    787                 func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save)
    788             else:
    789                 func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save)
    790 
    791     def get_directory_name(self):
    792         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
    793 
    794     def get_filename(self, filename):
    795         from django.utils.text import get_valid_filename
    796         f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
    797         return os.path.normpath(f)
    798 
    799     def save_form_data(self, instance, data):
    800         from django.newforms.fields import UploadedFile
    801         if data and isinstance(data, UploadedFile):
    802             getattr(instance, "save_%s_file" % self.name)(data.filename, data.content, save=False)
    803 
    804     def formfield(self, **kwargs):
    805         defaults = {'form_class': forms.FileField}
    806         # If a file has been provided previously, then the form doesn't require
    807         # that a new file is provided this time.
    808         # The code to mark the form field as not required is used by
    809         # form_for_instance, but can probably be removed once form_for_instance
    810         # is gone. ModelForm uses a different method to check for an existing file.
    811         if 'initial' in kwargs:
    812             defaults['required'] = False
    813         defaults.update(kwargs)
    814         return super(FileField, self).formfield(**defaults)
    815 
    816711class FilePathField(Field):
    817712    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
    818713        self.path, self.match, self.recursive = path, match, recursive
     
    833728        defaults.update(kwargs)
    834729        return super(FloatField, self).formfield(**defaults)
    835730
    836 class ImageField(FileField):
    837     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
    838         self.width_field, self.height_field = width_field, height_field
    839         FileField.__init__(self, verbose_name, name, **kwargs)
    840 
    841     def get_manipulator_field_objs(self):
    842         return [oldforms.ImageUploadField, oldforms.HiddenField]
    843 
    844     def contribute_to_class(self, cls, name):
    845         super(ImageField, self).contribute_to_class(cls, name)
    846         # Add get_BLAH_width and get_BLAH_height methods, but only if the
    847         # image field doesn't have width and height cache fields.
    848         if not self.width_field:
    849             setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))
    850         if not self.height_field:
    851             setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
    852 
    853     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
    854         FileField.save_file(self, new_data, new_object, original_object, change, rel, save)
    855         # If the image has height and/or width field(s) and they haven't
    856         # changed, set the width and/or height field(s) back to their original
    857         # values.
    858         if change and (self.width_field or self.height_field) and save:
    859             if self.width_field:
    860                 setattr(new_object, self.width_field, getattr(original_object, self.width_field))
    861             if self.height_field:
    862                 setattr(new_object, self.height_field, getattr(original_object, self.height_field))
    863             new_object.save()
    864 
    865     def formfield(self, **kwargs):
    866         defaults = {'form_class': forms.ImageField}
    867         defaults.update(kwargs)
    868         return super(ImageField, self).formfield(**defaults)
    869 
    870731class IntegerField(Field):
    871732    empty_strings_allowed = False
    872733    def get_manipulator_field_objs(self):
  • django/db/models/fields/files.py

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

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

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

     
    18711871get_FOO_filename()
    18721872------------------
    18731873
     1874**Deprecated in Django development version. See `managing files`_ for the new,
     1875preferred method for dealing with files.**
     1876
    18741877For every ``FileField``, the object will have a ``get_FOO_filename()`` method,
    18751878where ``FOO`` is the name of the field. This returns the full filesystem path
    18761879to the file, according to your ``MEDIA_ROOT`` setting.
     
    18811884get_FOO_url()
    18821885-------------
    18831886
     1887**Deprecated in Django development version. See `managing files`_ for the new,
     1888preferred method for dealing with files.**
     1889
    18841890For every ``FileField``, the object will have a ``get_FOO_url()`` method,
    18851891where ``FOO`` is the name of the field. This returns the full URL to the file,
    18861892according to your ``MEDIA_URL`` setting. If the value is blank, this method
     
    18891895get_FOO_size()
    18901896--------------
    18911897
     1898**Deprecated in Django development version. See `managing files`_ for the new,
     1899preferred method for dealing with files.**
     1900
    18921901For every ``FileField``, the object will have a ``get_FOO_size()`` method,
    18931902where ``FOO`` is the name of the field. This returns the size of the file, in
    18941903bytes. (Behind the scenes, it uses ``os.path.getsize``.)
     
    18961905save_FOO_file(filename, raw_contents)
    18971906-------------------------------------
    18981907
     1908**Deprecated in Django development version. See `managing files`_ for the new,
     1909preferred method for dealing with files.**
     1910
    18991911For every ``FileField``, the object will have a ``save_FOO_file()`` method,
    19001912where ``FOO`` is the name of the field. This saves the given file to the
    19011913filesystem, using the given filename. If a file with the given filename already
     
    19051917get_FOO_height() and get_FOO_width()
    19061918------------------------------------
    19071919
     1920**Deprecated in Django development version. See `managing files`_ for the new,
     1921preferred method for dealing with files.**
     1922
    19081923For every ``ImageField``, the object will have ``get_FOO_height()`` and
    19091924``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
    19101925returns the height (or width) of the image, as an integer, in pixels.
    19111926
     1927.. _`managing files`: ../files/
     1928
    19121929Shortcuts
    19131930=========
    19141931
  • docs/files.txt

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

     
    230230``FileField``
    231231~~~~~~~~~~~~~
    232232
    233 A file-upload field. Has one **required** argument:
     233A file-upload field. **Requires** exactly one of the following two arguments:
    234234
    235235    ======================  ===================================================
    236236    Argument                Description
    237237    ======================  ===================================================
    238238    ``upload_to``           A local filesystem path that will be appended to
    239239                            your ``MEDIA_ROOT`` setting to determine the
    240                             output of the ``get_<fieldname>_url()`` helper
    241                             function.
     240                            final storage destination. If this argument is
     241                            supplied, the storage backend will default to
     242                            ``FileSystemBackend``.
     243    ``backend``             **New in Django development version**
     244
     245                            A storage backend object, which handles the storage
     246                            and retrieval of your files. See `managing files`_
     247                            for details on how to provide this object.
    242248    ======================  ===================================================
    243249
    244 This path may contain `strftime formatting`_, which will be replaced by the
    245 date/time of the file upload (so that uploaded files don't fill up the given
    246 directory).
     250.. _managing files: ../files/
    247251
     252The ``upload_to`` path may contain `strftime formatting`_, which will be
     253replaced by the date/time of the file upload (so that uploaded files don't fill
     254up the given directory).
     255
    248256The admin represents this field as an ``<input type="file">`` (a file-upload
    249257widget).
    250258
    251 Using a ``FileField`` or an ``ImageField`` (see below) in a model takes a few
    252 steps:
     259Using a ``FileField`` or an ``ImageField`` (see below) in a model without a
     260specified backend takes a few steps:
    253261
    254262    1. In your settings file, you'll need to define ``MEDIA_ROOT`` as the
    255263       full path to a directory where you'd like Django to store uploaded
  • tests/modeltests/files/models.py

    Property changes on: tests\modeltests\files
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
     
     1"""
     242. Storing files according to a custom backend
     3
     4FileField and its variations can take a "backend" argument to specify how and
     5where files should be stored.
     6"""
     7
     8import tempfile
     9
     10from django.db import models
     11from django.core.filestorage.filesystem import FileSystemBackend
     12
     13temp_dir = tempfile.gettempdir()
     14
     15class CustomBackend(FileSystemBackend):
     16    def get_available_filename(self, filename):
     17        # Append numbers to duplicate files rather than underscores, like Trac
     18
     19        parts = filename.split('.')
     20        basename, ext = parts[0], parts[1:]
     21        number = 2
     22
     23        while self.file_exists(filename):
     24            filename = '.'.join([basename, str(number)] + ext)
     25            number += 1
     26
     27        return filename
     28
     29standard_backend = FileSystemBackend(location=temp_dir)
     30custom_backend = CustomBackend(location=temp_dir)
     31
     32class Storage(models.Model):
     33    normal = models.FileField(backend=standard_backend, upload_to='tests')
     34    custom = models.FileField(backend=custom_backend, upload_to='tests')
     35
     36__test__ = {'API_TESTS':"""
     37# An object without a file has limited functionality
     38
     39>>> obj1 = Storage()
     40>>> obj1.normal
     41<File: >
     42>>> obj1.normal.get_filesize()
     43Traceback (most recent call last):
     44...
     45ValueError: The 'normal' attribute has no file associated with it.
     46
     47# Saving a file enables full functionality
     48
     49>>> obj1.normal.save_file('django_test.txt', 'content')
     50>>> obj1.normal
     51<File: tests/django_test.txt>
     52>>> obj1.normal.get_filesize()
     537
     54>>> obj1.normal.open_file().read()
     55'content'
     56
     57# Save another file with the same name
     58
     59>>> obj2 = Storage()
     60>>> obj2.normal.save_file('django_test.txt', 'more content')
     61>>> obj2.normal
     62<File: tests/django_test_.txt>
     63>>> obj2.normal.get_filesize()
     6412
     65
     66# Custom backends can behave differently
     67
     68>>> obj1.custom.save_file('django_test.txt', 'trac-style filenames')
     69>>> obj1.custom
     70<File: tests/django_test.2.txt>
     71>>> obj2.custom.save_file('django_test.txt', 'another file')
     72>>> obj2.custom
     73<File: tests/django_test.3.txt>
     74
     75# Clean up the temporary files
     76
     77>>> obj1.normal.delete_file()
     78>>> obj1.custom.delete_file()
     79>>> obj2.normal.delete_file()
     80>>> obj2.custom.delete_file()
     81"""}
  • tests/modeltests/files/models.py

     
     1"""
     242. Storing files according to a custom backend
     3
     4FileField and its variations can take a "backend" argument to specify how and
     5where files should be stored.
     6"""
     7
     8import tempfile
     9
     10from django.db import models
     11from django.core.filestorage.filesystem import FileSystemBackend
     12
     13temp_dir = tempfile.gettempdir()
     14
     15class CustomBackend(FileSystemBackend):
     16    def get_available_filename(self, filename):
     17        # Append numbers to duplicate files rather than underscores, like Trac
     18
     19        parts = filename.split('.')
     20        basename, ext = parts[0], parts[1:]
     21        number = 2
     22
     23        while self.file_exists(filename):
     24            filename = '.'.join([basename, str(number)] + ext)
     25            number += 1
     26
     27        return filename
     28
     29standard_backend = FileSystemBackend(location=temp_dir)
     30custom_backend = CustomBackend(location=temp_dir)
     31
     32class Storage(models.Model):
     33    normal = models.FileField(backend=standard_backend, upload_to='tests')
     34    custom = models.FileField(backend=custom_backend, upload_to='tests')
     35
     36__test__ = {'API_TESTS':"""
     37# An object without a file has limited functionality
     38
     39>>> obj1 = Storage()
     40>>> obj1.normal
     41<File: >
     42>>> obj1.normal.get_filesize()
     43Traceback (most recent call last):
     44...
     45ValueError: The 'normal' attribute has no file associated with it.
     46
     47# Saving a file enables full functionality
     48
     49>>> obj1.normal.save_file('django_test.txt', 'content')
     50>>> obj1.normal
     51<File: tests/django_test.txt>
     52>>> obj1.normal.get_filesize()
     537
     54>>> obj1.normal.open_file().read()
     55'content'
     56
     57# Save another file with the same name
     58
     59>>> obj2 = Storage()
     60>>> obj2.normal.save_file('django_test.txt', 'more content')
     61>>> obj2.normal
     62<File: tests/django_test_.txt>
     63>>> obj2.normal.get_filesize()
     6412
     65
     66# Custom backends can behave differently
     67
     68>>> obj1.custom.save_file('django_test.txt', 'trac-style filenames')
     69>>> obj1.custom
     70<File: tests/django_test.2.txt>
     71>>> obj2.custom.save_file('django_test.txt', 'another file')
     72>>> obj2.custom
     73<File: tests/django_test.3.txt>
     74
     75# Clean up the temporary files
     76
     77>>> obj1.normal.delete_file()
     78>>> obj1.custom.delete_file()
     79>>> obj2.normal.delete_file()
     80>>> obj2.custom.delete_file()
     81"""}
  • tests/modeltests/model_forms/models.py

     
    743743<class 'django.newforms.fields.UploadedFile'>
    744744>>> instance = f.save()
    745745>>> instance.file
    746 u'.../test1.txt'
     746<File: .../test1.txt>
    747747
    748748# Edit an instance that already has the file defined in the model. This will not
    749749# save the file again, but leave it exactly as it is.
     
    752752>>> f.is_valid()
    753753True
    754754>>> f.cleaned_data['file']
    755 u'.../test1.txt'
     755<File: .../test1.txt>
    756756>>> instance = f.save()
    757757>>> instance.file
    758 u'.../test1.txt'
     758<File: .../test1.txt>
    759759
    760760# Delete the current file since this is not done by Django.
    761761
    762 >>> os.unlink(instance.get_file_filename())
     762>>> instance.file.delete_file()
    763763
    764764# Override the file by uploading a new one.
    765765
     
    768768True
    769769>>> instance = f.save()
    770770>>> instance.file
    771 u'.../test2.txt'
     771<File: .../test2.txt>
    772772
    773773>>> instance.delete()
    774774
     
    780780True
    781781>>> instance = f.save()
    782782>>> instance.file
    783 ''
     783<File: >
    784784
    785785>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance)
    786786>>> f.is_valid()
    787787True
    788788>>> instance = f.save()
    789789>>> instance.file
    790 u'.../test3.txt'
     790<File: .../test3.txt>
    791791>>> instance.delete()
    792792
    793793# ImageField ###################################################################
     
    809809<class 'django.newforms.fields.UploadedFile'>
    810810>>> instance = f.save()
    811811>>> instance.image
    812 u'.../test.png'
     812<File: .../test.png>
    813813
    814814# Edit an instance that already has the image defined in the model. This will not
    815815# save the image again, but leave it exactly as it is.
     
    818818>>> f.is_valid()
    819819True
    820820>>> f.cleaned_data['image']
    821 u'.../test.png'
     821<File: .../test.png>
    822822>>> instance = f.save()
    823823>>> instance.image
    824 u'.../test.png'
     824<File: .../test.png>
    825825
    826826# Delete the current image since this is not done by Django.
    827827
    828 >>> os.unlink(instance.get_image_filename())
     828>>> instance.image.delete_file()
    829829
    830830# Override the file by uploading a new one.
    831831
     
    834834True
    835835>>> instance = f.save()
    836836>>> instance.image
    837 u'.../test2.png'
     837<File: .../test2.png>
    838838
    839839>>> instance.delete()
    840840
     
    846846True
    847847>>> instance = f.save()
    848848>>> instance.image
    849 ''
     849<File: >
    850850
    851851>>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance)
    852852>>> f.is_valid()
    853853True
    854854>>> instance = f.save()
    855855>>> instance.image
    856 u'.../test3.png'
     856<File: .../test3.png>
    857857>>> instance.delete()
    858858
    859859"""}
  • tests/regressiontests/bug639/tests.py

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

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

     
    223223#     (pk_obj, 620, DatePKData, datetime.date(2006,6,16)),
    224224#     (pk_obj, 630, DateTimePKData, datetime.datetime(2006,6,16,10,42,37)),
    225225    (pk_obj, 640, EmailPKData, "hovercraft@example.com"),
    226     (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
     226#     (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
    227227    (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"),
    228228    (pk_obj, 670, DecimalPKData, decimal.Decimal('12.345')),
    229229    (pk_obj, 671, DecimalPKData, decimal.Decimal('-12.345')),
Back to Top