Django

Code

Changeset 8244

Show
Ignore:
Timestamp:
08/08/08 15:59:02 (4 months ago)
Author:
jacob
Message:

File storage refactoring, adding far more flexibility to Django's file handling. The new files.txt document has details of the new features.

This is a backwards-incompatible change; consult BackwardsIncompatibleChanges for details.

Fixes #3567, #3621, #4345, #5361, #5655, #7415.

Many thanks to Marty Alchin who did the vast majority of this work.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/conf/global_settings.py

    r8162 r8244  
    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/" 
  • django/trunk/django/contrib/admin/widgets.py

    r8240 r8244  
    8686        output = [] 
    8787        if value: 
    88             output.append('%s <a target="_blank" href="%s%s">%s</a> <br />%s ' % \ 
    89                 (_('Currently:'), settings.MEDIA_URL, value, value, _('Change:'))) 
     88            output.append('%s <a target="_blank" href="%s">%s</a> <br />%s ' % \ 
     89                (_('Currently:'), value.url, value, _('Change:'))) 
    9090        output.append(super(AdminFileWidget, self).render(name, value, attrs)) 
    9191        return mark_safe(u''.join(output)) 
  • django/trunk/django/core/files/__init__.py

    r7814 r8244  
     1from django.core.files.base import File 
  • django/trunk/django/core/files/uploadedfile.py

    r8217 r8244  
    1111 
    1212from django.conf import settings 
     13from django.core.files.base import File 
    1314 
    1415from django.core.files import temp as tempfile 
     
    4041        return property(getter, setter) 
    4142 
    42 class UploadedFile(object): 
     43class UploadedFile(File): 
    4344    """ 
    4445    A abstract uploaded file (``TemporaryUploadedFile`` and 
     
    7677 
    7778    name = property(_get_name, _set_name) 
    78  
    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 
    9579 
    9680    # Deprecated properties 
     
    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. 
     
    131103    def close(self): 
    132104        pass 
    133  
    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_ 
    160105 
    161106    # Backwards-compatible support for uploaded-files-as-dictionaries. 
  • django/trunk/django/db/models/base.py

    r8223 r8244  
    44import os 
    55from itertools import izip 
     6from warnings import warn 
    67try: 
    78    set 
     
    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 
     
    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)) 
    478  
    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)         
    486  
    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             file_move_safe(raw_field.temporary_file_path(), full_filename) 
    531             raw_field.close() 
    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() 
     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 
     486 
     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) 
    554491 
    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] 
    562  
    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) 
     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() 
    570503 
    571504 
  • django/trunk/django/db/models/fields/__init__.py

    r8229 r8244  
    1111from django.db.models import signals 
    1212from django.db.models.query_utils import QueryWrapper 
     13from django.dispatch import dispatcher 
    1314from django.conf import settings 
    1415from django.core import validators 
     
    758759        return super(EmailField, self).formfield(**defaults) 
    759760 
    760 class FileField(Field): 
    761     def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): 
    762         self.upload_to = upload_to 
    763         kwargs['max_length'] = kwargs.get('max_length', 100) 
    764         Field.__init__(self, verbose_name, name, **kwargs) 
    765  
    766     def get_internal_type(self): 
    767         return "FileField" 
    768  
    769     def get_db_prep_value(self, value): 
    770         "Returns field's value prepared for saving into a database." 
    771         # Need to convert UploadedFile objects provided via a form to unicode for database insertion 
    772         if hasattr(value, 'name'): 
    773             return value.name 
    774         elif value is None: 
    775             return None 
    776         else: 
    777             return unicode(value) 
    778  
    779     def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True): 
    780         field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) 
    781         if not self.blank: 
    782             if rel: 
    783                 # This validator makes sure FileFields work in a related context. 
    784                 class RequiredFileField(object): 
    785                     def __init__(self, other_field_names, other_file_field_name): 
    786                         self.other_field_names = other_field_names 
    787                         self.other_file_field_name = other_file_field_name 
    788                         self.always_test = True 
    789                     def __call__(self, field_data, all_data): 
    790                         if not all_data.get(self.other_file_field_name, False): 
    791                             c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, ugettext_lazy("This field is required.")) 
    792                             c(field_data, all_data) 
    793                 # First, get the core fields, if any. 
    794                 core_field_names = [] 
    795                 for f in opts.fields: 
    796                     if f.core and f != self: 
    797                         core_field_names.extend(f.get_manipulator_field_names(name_prefix)) 
    798                 # Now, if there are any, add the validator to this FormField. 
    799                 if core_field_names: 
    800                     field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name)) 
    801             else: 
    802                 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, ugettext_lazy("This field is required.")) 
    803                 v.always_test = True 
    804                 field_list[0].validator_list.append(v) 
    805                 field_list[0].is_required = field_list[1].is_required = False 
    806  
    807         # If the raw path is passed in, validate it's under the MEDIA_ROOT. 
    808         def isWithinMediaRoot(field_data, all_data): 
    809             f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data)) 
    810             if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))): 
    811                 raise validators.ValidationError, _("Enter a valid filename.") 
    812         field_list[1].validator_list.append(isWithinMediaRoot) 
    813         return field_list 
    814  
    815     def contribute_to_class(self, cls, name): 
    816         super(FileField, self).contribute_to_class(cls, name) 
    817         setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self)) 
    818         setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) 
    819         setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 
    820         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) 
    821         signals.post_delete.connect(self.delete_file, sender=cls) 
    822  
    823     def delete_file(self, instance, **kwargs): 
    824         if getattr(instance, self.attname): 
    825             file_name = getattr(instance, 'get_%s_filename' % self.name)() 
    826             # If the file exists and no other object of this type references it, 
    827             # delete it from the filesystem. 
    828             if os.path.exists(file_name) and \ 
    829                 not instance.__class__._default_manager.filter(**{'%s__exact' % self.name: getattr(instance, self.attname)}): 
    830                 os.remove(file_name) 
    831  
    832     def get_manipulator_field_objs(self): 
    833         return [oldforms.FileUploadField, oldforms.HiddenField] 
    834  
    835     def get_manipulator_field_names(self, name_prefix): 
    836         return [name_prefix + self.name + '_file', name_prefix + self.name] 
    837  
    838     def save_file(self, new_data, new_object, original_object, change, rel, save=True): 
    839         upload_field_name = self.get_manipulator_field_names('')[0] 
    840         if new_data.get(upload_field_name, False): 
    841             if rel: 
    842                 file = new_data[upload_field_name][0] 
    843             else: 
    844                 file = new_data[upload_field_name] 
    845  
    846             if not file: 
    847                 return 
    848  
    849             # Backwards-compatible support for files-as-dictionaries. 
    850             # We don't need to raise a warning because Model._save_FIELD_file will 
    851             # do so for us. 
    852             try: 
    853                 file_name = file.name 
    854             except AttributeError: 
    855                 file_name = file['filename'] 
    856  
    857             func = getattr(new_object, 'save_%s_file' % self.name) 
    858             func(file_name, file, save) 
    859  
    860     def get_directory_name(self): 
    861         return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to)))) 
    862  
    863     def get_filename(self, filename): 
    864         from django.utils.text import get_valid_filename 
    865         f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename))) 
    866         return os.path.normpath(f) 
    867  
    868     def save_form_data(self, instance, data): 
    869         from django.core.files.uploadedfile import UploadedFile 
    870         if data and isinstance(data, UploadedFile): 
    871             getattr(instance, "save_%s_file" % self.name)(data.name, data, save=False) 
    872  
    873     def formfield(self, **kwargs): 
    874         defaults = {'form_class': forms.FileField} 
    875         # If a file has been provided previously, then the form doesn't require 
    876         # that a new file is provided this time. 
    877         # The code to mark the form field as not required is used by 
    878         # form_for_instance, but can probably be removed once form_for_instance 
    879         # is gone. ModelForm uses a different method to check for an existing file. 
    880         if 'initial' in kwargs: 
    881             defaults['required'] = False 
    882         defaults.update(kwargs) 
    883         return super(FileField, self).formfield(**defaults) 
    884  
    885761class FilePathField(Field): 
    886762    def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): 
     
    923799        defaults.update(kwargs) 
    924800        return super(FloatField, self).formfield(**defaults) 
    925  
    926 class ImageField(FileField): 
    927     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): 
    928         self.width_field, self.height_field = width_field, height_field 
    929         FileField.__init__(self, verbose_name, name, **kwargs) 
    930  
    931     def get_manipulator_field_objs(self): 
    932         return [oldforms.ImageUploadField, oldforms.HiddenField] 
    933  
    934     def contribute_to_class(self, cls, name): 
    935         super(ImageField, self).contribute_to_class(cls, name) 
    936         # Add get_BLAH_width and get_BLAH_height methods, but only if the 
    937         # image field doesn't have width and height cache fields. 
    938         if not self.width_field: 
    939             setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self)) 
    940         if not self.height_field: 
    941             setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self)) 
    942  
    943     def save_file(self, new_data, new_object, original_object, change, rel, save=True): 
    944         FileField.save_file(self, new_data, new_object, original_object, change, rel, save) 
    945         # If the image has height and/or width field(s) and they haven't 
    946         # changed, set the width and/or height field(s) back to their original 
    947         # values. 
    948         if change and (self.width_field or self.height_field) and save: 
    949             if self.width_field: 
    950                 setattr(new_object, self.width_field, getattr(original_object, self.width_field)) 
    951             if self.height_field: 
    952                 setattr(new_object, self.height_field, getattr(original_object, self.height_field)) 
    953             new_object.save() 
    954  
    955     def formfield(self, **kwargs): 
    956         defaults = {'form_class': forms.ImageField} 
    957         defaults.update(kwargs) 
    958         return super(ImageField, self).formfield(**defaults) 
    959801 
    960802class IntegerField(Field): 
  • django/trunk/django/db/models/__init__.py

    r8046 r8244  
    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 
  • django/trunk/django/db/models/manipulators.py

    r8223 r8244  
    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.db.models import signals 
    67from django.utils.functional import curry 
  • django/trunk/django/utils/images.py

    r4265 r8244  
    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) 
  • django/trunk/docs/custom_model_fields.txt

    r8152 r8244  
    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. 
  • django/trunk/docs/db-api.txt

    r8232 r8244  
    22992299------------------ 
    23002300 
    2301 For every ``FileField``, the object will have a ``get_FOO_filename()`` method, 
    2302 where ``FOO`` is the name of the field. This returns the full filesystem path 
    2303 to the file, according to your ``MEDIA_ROOT`` setting. 
    2304  
    2305 .. note:: 
    2306     It is only valid to call this method **after** saving the model when the 
    2307     field has been set. Prior to saving, the value returned will not contain 
    2308     the upload directory (the `upload_to` parameter) in the path. 
    2309  
    2310 Note that ``ImageField`` is technically a subclass of ``FileField``, so every 
    2311 model with an ``ImageField`` will also get this method. 
     2301**Deprecated in Django development version**; use ``object.FOO.name`` instead. 
     2302See `managing files`_ for details. 
    23122303 
    23132304get_FOO_url() 
    23142305------------- 
    23152306 
    2316 For every ``FileField``, the object will have a ``get_FOO_url()`` method, 
    2317 where ``FOO`` is the name of the field. This returns the full URL to the file, 
    2318 according to your ``MEDIA_URL`` setting. If the value is blank, this method 
    2319 returns an empty string. 
    2320  
    2321 .. note:: 
    2322     As with ``get_FOO_filename()``, it is only valid to call this method 
    2323     **after** saving the model, otherwise an incorrect result will be 
    2324     returned. 
     2307**Deprecated in Django development version**; use ``object.FOO.url`` instead. 
     2308See `managing files`_ for details. 
    23252309 
    23262310get_FOO_size() 
    23272311-------------- 
    23282312 
    2329 For every ``FileField``, the object will have a ``get_FOO_size()`` method, 
    2330 where ``FOO`` is the name of the field. This returns the size of the file, in 
    2331 bytes. (Behind the scenes, it uses ``os.path.getsize``.) 
     2313**Deprecated in Django development version**; use ``object.FOO.size`` instead. 
     2314See `managing files`_ for details. 
    23322315 
    23332316save_FOO_file(filename, raw_contents) 
    23342317------------------------------------- 
    23352318 
    2336 For every ``FileField``, the object will have a ``save_FOO_file()`` method, 
    2337 where ``FOO`` is the name of the field. This saves the given file to the 
    2338 filesystem, using the given filename. If a file with the given filename already 
    2339 exists, Django adds an underscore to the end of the filename (but before the 
    2340 extension) until the filename is available. 
     2319**Deprecated in Django development version**; use ``object.FOO.save()`` instead. 
     2320See `managing files`_ for details. 
    23412321 
    23422322get_FOO_height() and get_FOO_width() 
    23432323------------------------------------ 
    23442324 
    2345 For every ``ImageField``, the object will have ``get_FOO_height()`` and 
    2346 ``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This 
    2347 returns the height (or width) of the image, as an integer, in pixels. 
     2325**Deprecated in Django development version**; use ``object.FOO.width`` and 
     2326``object.FOO.height`` instead. See `managing files`_ for details. 
     2327 
     2328.. _`managing files`: ../files/ 
    23482329 
    23492330Shortcuts 
  • django/trunk/docs/model-api.txt

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

    r8147 r8244  
    426426isn't manually specified. Used with ``DEFAULT_CHARSET`` to construct the 
    427427``Content-Type`` header. 
     428 
     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/ 
    428438 
    429439DEFAULT_FROM_EMAIL 
  • django/trunk/docs/upload_handling.txt

    r8092 r8244  
    156156======================== 
    157157 
    158 All ``UploadedFile`` objects define the following methods/attributes: 
    159  
    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. 
     158In addition to those inherited from `File`_, all ``UploadedFile`` objects define 
     159the following methods/attributes: 
    177160 
    178161    ``UploadedFile.content_type`` 
     
    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 
  • django/trunk/tests/modeltests/model_forms/models.py

    r8016 r8244  
    1212 
    1313from django.db import models 
     14from django.core.files.storage import FileSystemStorage 
     15 
     16temp_storage = FileSystemStorage(tempfile.gettempdir()) 
    1417 
    1518ARTICLE_STATUS = ( 
     
    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): 
     
    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): 
     
    787790# FileField ################################################################### 
    788791 
     792# File forms. 
     793 
    789794>>> class TextFileForm(ModelForm): 
    790795...     class Meta: 
     
    809814>>> instance = f.save() 
    810815>>> instance.file 
    811 u'...test1.txt' 
    812  
    813 >>> os.unlink(instance.get_file_filename()
     816<FieldFile: tests/test1.txt> 
     817 
     818>>> instance.file.delete(
    814819 
    815820>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) 
     
    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 
     
    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. 
     
    844849>>> instance = f.save() 
    845850>>> instance.file 
    846 u'...test2.txt' 
     851<FieldFile: tests/test2.txt>