Django

Code

Changeset 7814

Show
Ignore:
Timestamp:
07/01/08 10:10:51 (2 months ago)
Author:
jacob
Message:

Fixed #2070: refactored Django's file upload capabilities.

A description of the new features can be found in the new upload handling documentation; the executive summary is that Django will now happily handle uploads of large files without issues.

This changes the representation of uploaded files from dictionaries to bona fide objects; see BackwardsIncompatibleChanges for details.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/AUTHORS

    r7800 r7814  
    6060    av0000@mail.ru 
    6161    David Avsajanishvili <avsd05@gmail.com> 
    62     axiak@mit.edu 
     62    Mike Axiak <axiak@mit.edu> 
    6363    Niran Babalola <niran@niran.org> 
    6464    Morten Bagai <m@bagai.com> 
     
    142142    Szilveszter Farkas <szilveszter.farkas@gmail.com> 
    143143    favo@exoweb.net 
     144    fdr <drfarina@gmail.com> 
    144145    Dmitri Fedortchenko <zeraien@gmail.com> 
     146    Jonathan Feignberg <jdf@pobox.com> 
    145147    Liang Feng <hutuworm@gmail.com> 
    146148    Bill Fenner <fenner@gmail.com> 
  • django/trunk/django/conf/global_settings.py

    r7698 r7814  
    232232MEDIA_URL = '' 
    233233 
     234# List of upload handler classes to be applied in order. 
     235FILE_UPLOAD_HANDLERS = ( 
     236    'django.core.files.uploadhandler.MemoryFileUploadHandler', 
     237    'django.core.files.uploadhandler.TemporaryFileUploadHandler', 
     238) 
     239 
     240# Maximum size, in bytes, of a request before it will be streamed to the 
     241# file system instead of into memory. 
     242FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB 
     243 
     244# Directory in which upload streamed files will be temporarily saved. A value of 
     245# `None` will make Django use the operating system's default temporary directory 
     246# (i.e. "/tmp" on *nix systems). 
     247FILE_UPLOAD_TEMP_DIR = None 
     248 
    234249# Default formatting for date objects. See all available format strings here: 
    235250# http://www.djangoproject.com/documentation/templates/#now 
  • django/trunk/django/core/handlers/modpython.py

    r7200 r7814  
    5454        "Populates self._post and self._files" 
    5555        if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'): 
    56             self._post, self._files = http.parse_file_upload(self._req.headers_in, self.raw_post_data) 
     56            self._raw_post_data = '' 
     57            self._post, self._files = self.parse_file_upload(self.META, self._req) 
    5758        else: 
    5859            self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 
  • django/trunk/django/core/handlers/wsgi.py

    r6826 r7814  
    113113        if self.method == 'POST': 
    114114            if self.environ.get('CONTENT_TYPE', '').startswith('multipart'): 
    115                 header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')]) 
    116                 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '') 
    117                 self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data) 
     115                self._raw_post_data = '' 
     116                self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input']) 
    118117            else: 
    119118                self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() 
  • django/trunk/django/db/models/base.py

    r7795 r7814  
    2020from django.utils.functional import curry 
    2121from django.utils.encoding import smart_str, force_unicode, smart_unicode 
     22from django.core.files.move import file_move_safe 
     23from django.core.files import locks 
    2224from django.conf import settings 
    2325 
     
    470472        return os.path.getsize(self._get_FIELD_filename(field)) 
    471473 
    472     def _save_FIELD_file(self, field, filename, raw_contents, save=True): 
     474    def _save_FIELD_file(self, field, filename, raw_field, save=True): 
    473475        directory = field.get_directory_name() 
    474476        try: # Create the date-based directory if it doesn't exist. 
     
    476478        except OSError: # Directory probably already exists. 
    477479            pass 
     480 
     481        # 
     482        # Check for old-style usage (files-as-dictionaries). Warn here first 
     483        # since there are multiple locations where we need to support both new 
     484        # and old usage. 
     485        # 
     486        if isinstance(raw_field, dict): 
     487            import warnings 
     488            warnings.warn( 
     489                message = "Representing uploaded files as dictionaries is"\ 
     490                          " deprected. Use django.core.files.SimpleUploadedFile"\ 
     491                          " instead.", 
     492                category = DeprecationWarning, 
     493                stacklevel = 2 
     494            ) 
     495            from django.core.files.uploadedfile import SimpleUploadedFile 
     496            raw_field = SimpleUploadedFile.from_dict(raw_field) 
     497 
     498        elif isinstance(raw_field, basestring): 
     499            import warnings 
     500            warnings.warn( 
     501                message = "Representing uploaded files as strings is "\ 
     502                          " deprecated. Use django.core.files.SimpleUploadedFile "\ 
     503                          " 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 
    478513        filename = field.get_filename(filename) 
    479514 
     515        # 
    480516        # If the filename already exists, keep adding an underscore to the name of 
    481517        # the file until the filename doesn't exist. 
     518        # 
    482519        while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)): 
    483520            try: 
     
    487524            else: 
    488525                filename = filename[:dot_index] + '_' + filename[dot_index:] 
    489  
    490         # Write the file to disk. 
     526        # 
     527        # Save the file name on the object and write the file to disk 
     528        # 
     529 
    491530        setattr(self, field.attname, filename) 
    492531 
    493532        full_filename = self._get_FIELD_filename(field) 
    494         fp = open(full_filename, 'wb') 
    495         fp.write(raw_contents) 
    496         fp.close() 
     533 
     534        if hasattr(raw_field, 'temporary_file_path'): 
     535            # This file has a file path that we can move. 
     536            raw_field.close() 
     537            file_move_safe(raw_field.temporary_file_path(), full_filename) 
     538 
     539        else: 
     540            # This is a normal uploadedfile that we can stream. 
     541            fp = open(full_filename, 'wb') 
     542            locks.lock(fp, locks.LOCK_EX) 
     543            for chunk in raw_field.chunk(): 
     544                fp.write(chunk) 
     545            locks.unlock(fp) 
     546            fp.close() 
    497547 
    498548        # Save the width and/or height, if applicable. 
  • django/trunk/django/db/models/fields/__init__.py

    r7797 r7814  
    812812        setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) 
    813813        setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 
    814         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_contents, save=True: instance._save_FIELD_file(self, filename, raw_contents, save)) 
     814        setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) 
    815815        dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) 
    816816 
     
    835835            func = getattr(new_object, 'save_%s_file' % self.name) 
    836836            if rel: 
    837                 func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"], save) 
     837                file = new_data[upload_field_name][0] 
    838838            else: 
    839                 func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"], save) 
     839                file = new_data[upload_field_name] 
     840 
     841            # Backwards-compatible support for files-as-dictionaries. 
     842            # We don't need to raise a warning because Model._save_FIELD_file will 
     843            # do so for us. 
     844            try: 
     845                file_name = file.file_name 
     846            except AttributeError: 
     847                file_name = file['filename'] 
     848 
     849            func(file_name, file, save) 
    840850 
    841851    def get_directory_name(self): 
     
    850860        from django.newforms.fields import UploadedFile 
    851861        if data and isinstance(data, UploadedFile): 
    852             getattr(instance, "save_%s_file" % self.name)(data.filename, data.content, save=False) 
     862            getattr(instance, "save_%s_file" % self.name)(data.filename, data.data, save=False) 
    853863 
    854864    def formfield(self, **kwargs): 
  • django/trunk/django/http/__init__.py

    r7334 r7814  
    1010    from cgi import parse_qsl 
    1111 
    12 from django.utils.datastructures import MultiValueDict, FileDic
     12from django.utils.datastructures import MultiValueDict, ImmutableLis
    1313from django.utils.encoding import smart_str, iri_to_uri, force_unicode 
    14  
     14from django.http.multipartparser import MultiPartParser 
     15from django.conf import settings 
     16from django.core.files import uploadhandler 
    1517from utils import * 
    1618 
    1719RESERVED_CHARS="!*'();:@&=+$,/?%#[]" 
    18  
    1920 
    2021class Http404(Exception): 
     
    2627    # The encoding used in GET/POST dicts. None means use default setting. 
    2728    _encoding = None 
     29    _upload_handlers = [] 
    2830 
    2931    def __init__(self): 
     
    103105    encoding = property(_get_encoding, _set_encoding) 
    104106 
    105 def parse_file_upload(header_dict, post_data): 
    106     """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 
    107     import email, email.Message 
    108     from cgi import parse_header 
    109     raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()]) 
    110     raw_message += '\r\n\r\n' + post_data 
    111     msg = email.message_from_string(raw_message) 
    112     POST = QueryDict('', mutable=True) 
    113     FILES = MultiValueDict() 
    114     for submessage in msg.get_payload(): 
    115         if submessage and isinstance(submessage, email.Message.Message): 
    116             name_dict = parse_header(submessage['Content-Disposition'])[1] 
    117             # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads 
    118             # or {'name': 'blah'} for POST fields 
    119             # We assume all uploaded files have a 'filename' set. 
    120             if 'filename' in name_dict: 
    121                 assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported" 
    122                 if not name_dict['filename'].strip(): 
    123                     continue 
    124                 # IE submits the full path, so trim everything but the basename. 
    125                 # (We can't use os.path.basename because that uses the server's 
    126                 # directory separator, which may not be the same as the 
    127                 # client's one.) 
    128                 filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:] 
    129                 FILES.appendlist(name_dict['name'], FileDict({ 
    130                     'filename': filename, 
    131                     'content-type': 'Content-Type' in submessage and submessage['Content-Type'] or None, 
    132                     'content': submessage.get_payload(), 
    133                 })) 
    134             else: 
    135                 POST.appendlist(name_dict['name'], submessage.get_payload()) 
    136     return POST, FILES 
    137  
     107    def _initialize_handlers(self): 
     108        self._upload_handlers = [uploadhandler.load_handler(handler, self) 
     109                                 for handler in settings.FILE_UPLOAD_HANDLERS] 
     110 
     111    def _set_upload_handlers(self, upload_handlers): 
     112        if hasattr(self, '_files'): 
     113            raise AttributeError("You cannot set the upload handlers after the upload has been processed.") 
     114        self._upload_handlers = upload_handlers 
     115 
     116    def _get_upload_handlers(self): 
     117        if not self._upload_handlers: 
     118            # If thre are no upload handlers defined, initialize them from settings. 
     119            self._initialize_handlers() 
     120        return self._upload_handlers 
     121 
     122    upload_handlers = property(_get_upload_handlers, _set_upload_handlers) 
     123 
     124    def parse_file_upload(self, META, post_data): 
     125        """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" 
     126        self.upload_handlers = ImmutableList( 
     127            self.upload_handlers, 
     128            warning = "You cannot alter upload handlers after the upload has been processed." 
     129        ) 
     130        parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) 
     131        return parser.parse() 
    138132 
    139133class QueryDict(MultiValueDict): 
  • django/trunk/django/newforms/fields.py

    r7799 r7814  
    88import re 
    99import time 
     10try: 
     11    from cStringIO import StringIO 
     12except ImportError: 
     13    from StringIO import StringIO 
     14 
    1015# Python 2.3 fallbacks 
    1116try: 
     
    417422class UploadedFile(StrAndUnicode): 
    418423    "A wrapper for files uploaded in a FileField" 
    419     def __init__(self, filename, content): 
     424    def __init__(self, filename, data): 
    420425        self.filename = filename 
    421         self.content = content 
     426        self.data = data 
    422427 
    423428    def __unicode__(self): 
     
    445450        elif not data and initial: 
    446451            return initial 
     452 
     453        if isinstance(data, dict): 
     454            # We warn once, then support both ways below. 
     455            import warnings 
     456            warnings.warn( 
     457                message = "Representing uploaded files as dictionaries is"\ 
     458                          " deprecated. Use django.core.files.SimpleUploadedFile "\ 
     459                          " instead.", 
     460                category = DeprecationWarning, 
     461                stacklevel = 2 
     462            ) 
     463 
    447464        try: 
    448             f = UploadedFile(data['filename'], data['content']) 
    449         except TypeError: 
     465            file_name = data.file_name 
     466            file_size = data.file_size 
     467        except AttributeError: 
     468            try: 
     469                file_name = data.get('filename') 
     470                file_size = bool(data['content']) 
     471            except (AttributeError, KeyError): 
     472                raise ValidationError(self.error_messages['invalid']) 
     473 
     474        if not file_name: 
    450475            raise ValidationError(self.error_messages['invalid']) 
    451         except KeyError: 
    452             raise ValidationError(self.error_messages['missing']) 
    453         if not f.content: 
     476        if not file_size: 
    454477            raise ValidationError(self.error_messages['empty']) 
    455         return f 
     478 
     479        return UploadedFile(file_name, data) 
    456480 
    457481class ImageField(FileField): 
     
    471495            return initial 
    472496        from PIL import Image 
    473         from cStringIO import StringIO 
     497 
     498        # We need to get a file object for PIL. We might have a path or we might 
     499        # have to read the data into memory. 
     500        if hasattr(data, 'temporary_file_path'): 
     501            file = data.temporary_file_path() 
     502        else: 
     503            if hasattr(data, 'read'): 
     504                file = StringIO(data.read()) 
     505            else: 
     506                file = StringIO(data['content']) 
     507 
    474508        try: 
    475509            # load() is the only method that can spot a truncated JPEG, 
    476510            #  but it cannot be called sanely after verify() 
    477             trial_image = Image.open(StringIO(f.content)
     511            trial_image = Image.open(file
    478512            trial_image.load() 
     513 
     514            # Since we're about to use the file again we have to reset the 
     515            # file object if possible. 
     516            if hasattr(file, 'reset'): 
     517                file.reset() 
     518 
    479519            # verify() is the only method that can spot a corrupt PNG, 
    480520            #  but it must be called immediately after the constructor 
    481             trial_image = Image.open(StringIO(f.content)
     521            trial_image = Image.open(file
    482522            trial_image.verify() 
    483523        except Exception: # Python Imaging Library doesn't recognize it as an image 
  • django/trunk/django/oldforms/__init__.py

    r6671 r7814  
    681681        self.validator_list = [self.isNonEmptyFile] + validator_list 
    682682 
    683     def isNonEmptyFile(self, field_data, all_data): 
    684         try: 
    685             content = field_data['content'] 
    686         except TypeError: 
    687             raise validators.CriticalValidationError, ugettext("No file was submitted. Check the encoding type on the form.") 
    688         if not content: 
     683    def isNonEmptyFile(self, new_data, all_data): 
     684        if hasattr(new_data, 'upload_errors'): 
     685            upload_errors = new_data.upload_errors() 
     686            if upload_errors: 
     687                raise validators.CriticalValidationError, upload_errors 
     688        try: 
     689            file_size = new_data.file_size 
     690        except AttributeError: 
     691            file_size = len(new_data['content']) 
     692        if not file_size: 
    689693            raise validators.CriticalValidationError, ugettext("The submitted file is empty.") 
    690694 
     
    692696        return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 
    693697            (self.get_id(), self.__class__.__name__, self.field_name)) 
     698 
     699    def prepare(self, new_data): 
     700        if hasattr(new_data, 'upload_errors'): 
     701            upload_errors = new_data.upload_errors() 
     702            new_data[self.field_name] = { '_file_upload_error': upload_errors } 
    694703 
    695704    def html2python(data): 
  • django/trunk/django/test/client.py

    r7583 r7814  
    22import sys 
    33import os 
    4 from cStringIO import StringIO 
     4try: 
     5    from cStringIO import StringIO 
     6except ImportError: 
     7    from StringIO import StringIO 
    58from django.conf import settings 
    69from django.contrib.auth import authenticate, login 
     
    1922BOUNDARY = 'BoUnDaRyStRiNg' 
    2023MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY 
     24 
     25class FakePayload(object): 
     26    """ 
     27    A wrapper around StringIO that restricts what can be read since data from 
     28    the network can't be seeked and cannot be read outside of its content 
     29    length. This makes sure that views can't do anything under the test client 
     30    that wouldn't work in Real Life. 
     31    """ 
     32    def __init__(self, content): 
     33        self.__content = StringIO(content) 
     34        self.__len = len(content) 
     35 
     36    def read(self, num_bytes=None): 
     37        if num_bytes is None: 
     38            num_bytes = self.__len or 1 
     39        assert self.__len >= num_bytes, "Cannot read more than the available bytes from the HTTP incoming data." 
     40        content = self.__content.read(num_bytes) 
     41        self.__len -= num_bytes 
     42        return content 
    2143 
    2244class ClientHandler(BaseHandler): 
     
    237259            'PATH_INFO':      urllib.unquote(path), 
    238260            'REQUEST_METHOD': 'POST', 
    239             'wsgi.input':     StringIO(post_data), 
     261            'wsgi.input':     FakePayload(post_data), 
    240262        } 
    241263        r.update(extra) 
  • django/trunk/django/utils/datastructures.py

    r7743 r7814  
    333333                current = {bits[-1]: v} 
    334334 
    335 class FileDict(dict): 
    336     """ 
    337     A dictionary used to hold uploaded file contents. The only special feature 
    338     here is that repr() of this object won't dump the entire contents of the 
    339     file to the output. A handy safeguard for a large file upload. 
    340     """ 
    341     def __repr__(self): 
    342         if 'content' in self: 
    343             d = dict(self, content='<omitted>') 
    344             return dict.__repr__(d) 
    345         return dict.__repr__(self) 
     335class ImmutableList(tuple): 
     336    """ 
     337    A tuple-like object that raises useful errors when it is asked to mutate. 
     338 
     339    Example:: 
     340 
     341        >>> a = ImmutableList(range(5), warning="You cannot mutate this.") 
     342        >>> a[3] = '4' 
     343        Traceback (most recent call last): 
     344            ... 
     345        AttributeError: You cannot mutate this. 
     346    """ 
     347 
     348    def __new__(cls, *args, **kwargs): 
     349        if 'warning' in kwargs: 
     350            warning = kwargs['warning'] 
     351            del kwargs['warning'] 
     352        else: 
     353            warning = 'ImmutableList object is immutable.' 
     354        self = tuple.__new__(cls, *args, **kwargs) 
     355        self.warning = warning 
     356        return self 
     357 
     358    def complain(self, *wargs, **kwargs): 
     359        if isinstance(self.warning, Exception): 
     360            raise self.warning 
     361        else: 
     362            raise AttributeError, self.warning 
     363 
     364    # All list mutation functions complain. 
     365    __delitem__  = complain 
     366    __delslice__ = complain 
     367    __iadd__     = complain 
     368    __imul__     = complain 
     369    __setitem__  = complain 
     370    __setslice__ = complain 
     371    append       = complain 
     372    extend       = complain 
     373    insert       = complain 
     374    pop          = complain 
     375    remove       = complain 
     376    sort         = complain 
     377    reverse      = complain 
    346378 
    347379class DictWrapper(dict): 
  • django/trunk/django/utils/text.py

    r7581 r7814  
    44from django.utils.functional import allow_lazy 
    55from django.utils.translation import ugettext_lazy 
     6from htmlentitydefs import name2codepoint 
    67 
    78# Capitalizes the first letter of a string. 
     
    223224smart_split = allow_lazy(smart_split, unicode) 
    224225 
     226def _replace_entity(match): 
     227     text = match.group(1) 
     228     if text[0] == u'#': 
     229         text = text[1:] 
     230         try: 
     231             if text[0] in u'xX': 
     232                 c = int(text[1:], 16) 
     233             else: 
     234                 c = int(text) 
     235             return unichr(c) 
     236         except ValueError: 
     237             return match.group(0) 
     238     else: 
     239         try: 
     240             return unichr(name2codepoint[text]) 
     241         except (ValueError, KeyError): 
     242             return match.group(0) 
     243 
     244_entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));") 
     245 
     246def unescape_entities(text): 
     247     return _entity_re.sub(_replace_entity, text) 
     248unescape_entities = allow_lazy(unescape_entities, unicode) 
  • django/trunk/docs/newforms.txt

    r7677 r7814  
    806806 
    807807    # Bound form with an image field 
     808    >>> from django.core.files.uploadedfile import SimpleUploadedFile 
    808809    >>> data = {'subject': 'hello', 
    809810    ...         'message': 'Hi there', 
    810811    ...         'sender': 'foo@example.com', 
    811812    ...         'cc_myself': True} 
    812     >>> file_data = {'mugshot': {'filename':'face.jpg' 
    813     ...                          'content': <file data>}} 
     813    >>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)} 
    814814    >>> f = ContactFormWithMugshot(data, file_data) 
    815815 
  • django/trunk/docs/request_response.txt

    r7510 r7814  
    8181 
    8282``FILES`` 
     83     
     84    .. admonition:: Changed in Django development version 
     85         
     86        In previous versions of Django, ``request.FILES`` contained 
     87        simple ``dict`` objects representing uploaded files. This is 
     88        no longer true -- files are represented by ``UploadedFile`` 
     89        objects as described below. 
     90         
     91        These ``UploadedFile`` objects will emulate the old-style ``dict`` 
     92        interface, but this is deprecated and will be removed in the next 
     93        release of Django. 
     94         
    8395    A dictionary-like object containing all uploaded files. Each key in 
    8496    ``FILES`` is the ``name`` from the ``<input type="file" name="" />``. Each 
    85     value in ``FILES`` is a standard Python dictionary with the following three 
    86     keys: 
    87  
    88         * ``filename`` -- The name of the uploaded file, as a Python string. 
    89         * ``content-type`` -- The content type of the uploaded file. 
    90         * ``content`` -- The raw content of the uploaded file. 
    91  
     97    value in ``FILES`` is an ``UploadedFile`` object containing the following 
     98    attributes: 
     99 
     100        * ``read(num_bytes=None)`` -- Read a number of bytes from the file. 
     101        * ``file_name`` -- The name of the uploaded file. 
     102        * ``file_size`` -- The size, in bytes, of the uploaded file. 
     103        * ``chunk()`` -- A generator that yields sequential chunks of data. 
     104 
     105    See `File Uploads`_ for more information.  
     106     
    92107    Note that ``FILES`` will only contain data if the request method was POST 
    93108    and the ``<form>`` that posted to the request had 
    94109    ``enctype="multipart/form-data"``. Otherwise, ``FILES`` will be a blank 
    95110    dictionary-like object. 
     111     
     112    .. _File Uploads: ../upload_handling/ 
    96113 
    97114``META`` 
  • django/trunk/docs/settings.txt

    r7702 r7814  
    280280The database backend to use. The build-in database backends are 
    281281``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, 
    282 ``'sqlite3'`` and ``'oracle'``. 
     282``'sqlite3'``, ``'oracle'``, and ``'oracle'``. 
    283283 
    284284In the Django development version, you can use a database backend that doesn't 
     
    530530The character encoding used to decode any files read from disk. This includes 
    531531template files and initial SQL data files. 
     532 
     533FILE_UPLOAD_HANDLERS 
     534-------------------- 
     535 
     536**New in Django development version** 
     537 
     538Default:: 
     539 
     540    ("django.core.files.fileuploadhandler.MemoryFileUploadHandler", 
     541     "django.core.files.fileuploadhandler.TemporaryFileUploadHandler",) 
     542 
     543A tuple of handlers to use for uploading. See `file uploads`_ for details. 
     544 
     545.. _file uploads: ../upload_handling/ 
     546 
     547FILE_UPLOAD_MAX_MEMORY_SIZE 
     548--------------------------- 
     549 
     550**New in Django development version** 
     551 
     552Default: ``2621440`` (i.e. 2.5 MB). 
     553 
     554The maximum size (in bytes) that an upload will be before it gets streamed to 
     555the file system. See `file uploads`_ for details. 
     556 
     557FILE_UPLOAD_TEMP_DIR 
     558-------------------- 
     559 
     560**New in Django development version** 
     561 
     562Default: ``None`` 
     563 
     564The directory to store data temporarily while uploading files. If ``None``, 
     565Django will use the standard temporary directory for the operating system. For 
     566example, this will default to '/tmp' on *nix-style operating systems. 
     567 
     568See `file uploads`_ for details. 
    532569 
    533570FIXTURE_DIRS 
  • django/trunk/tests/modeltests/model_forms/models.py

    r7335 r7814  
    6868class ImageFile(models.Model): 
    6969    description = models.CharField(max_length=20) 
    70     image = models.FileField(upload_to=tempfile.gettempdir()) 
     70    try: 
     71        # If PIL is available, try testing PIL. 
     72        # Otherwise, it's equivalent to TextFile above. 
     73        import Image 
     74        image = models.ImageField(upload_to=tempfile.gettempdir()) 
     75    except ImportError: 
     76        image = models.FileField(upload_to=tempfile.gettempdir()) 
    7177 
    7278    def __unicode__(self): 
     
    7682>>> from django import newforms as forms 
    7783>>> from django.newforms.models import ModelForm 
     84>>> from django.core.files.uploadedfile import SimpleUploadedFile 
    7885 
    7986The bare bones, absolutely nothing custom, basic case. 
     
    793800# Upload a file and ensure it all works as expected. 
    794801 
    795 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test1.txt', 'content': 'hello world'}}) 
     802>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) 
    796803>>> f.is_valid() 
    797804True 
     
    802809u'...test1.txt' 
    803810 
     811>>> os.unlink(instance.get_file_filename()) 
     812 
     813>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test1.txt', 'hello world')}) 
     814>>> f.is_valid() 
     815True 
     816>>> type(f.cleaned_data['file']) 
     817<class 'django.newforms.fields.UploadedFile'> 
     818>>> instance = f.save() 
     819>>> instance.file 
     820u'...test1.txt' 
     821 
    804822# Edit an instance that already has the file defined in the model. This will not 
    805823# save the file again, but leave it exactly as it is. 
     
    815833 
    816834# Delete the current file since this is not done by Django. 
    817  
    818835>>> os.unlink(instance.get_file_filename()) 
    819836 
    820837# Override the file by uploading a new one. 
    821838 
    822 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test2.txt', 'content': 'hello world'}}, instance=instance) 
     839>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}, instance=instance) 
    823840>>> f.is_valid() 
    824841True 
     
    827844u'...test2.txt' 
    828845 
     846# Delete the current file since this is not done by Django. 
     847>>> os.unlink(instance.get_file_filename()) 
     848 
     849>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test2.txt', 'hello world')}) 
     850>>> f.is_valid() 
     851True 
     852>>> instance = f.save() 
     853>>> instance.file 
     854u'...test2.txt' 
     855 
     856# Delete the current file since this is not done by Django. 
     857>>> os.unlink(instance.get_file_filename()) 
     858 
    829859>>> instance.delete() 
    830860 
     
    839869'' 
    840870 
    841 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': {'filename': 'test3.txt', 'content': 'hello world'}}, instance=instance) 
     871>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance) 
    842872>>> f.is_valid() 
    843873True 
     
    845875>>> instance.file 
    846876u'...test3.txt' 
     877 
     878# Delete the current file since this is not done by Django. 
     879>>> os.unlink(instance.get_file_filename()) 
     880>>> instance.delete() 
     881 
     882>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}) 
     883>>> f.is_valid() 
     884True 
     885>>> instance = f.save() 
     886>>> instance.file 
     887u'...test3.txt' 
     888 
     889# Delete the current file since this is not done by Django. 
     890>>> os.unlink(instance.get_file_filename()) 
    847891>>> instance.delete() 
    848892 
     
    859903>>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png")).read() 
    860904 
    861 >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': {'filename': 'test.png', 'content': image_data}}) 
     905>>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) 
    862906>>> f.is_valid() 
    863907True 
     
    868912u'...test.png' 
    869913 
     914# Delete the current file since this is not done by Django. 
     915>>> os.unlink(instance.get_image_filename()) 
     916 
     917>>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) 
     918>>> f.is_valid() 
     919True 
     920>>> type(f.cleaned_data['image']) 
     921<class 'django.newforms.fields.UploadedFile'> 
     922>>> instance = f.save() 
     923>>> instance.image 
     924u'...test.png' 
     925 
    870926# Edit an instance that already has the image defined in the model. This will not 
    871927# save the image again, but leave it exactly as it is. 
     
    886942# Override the file by uploading a new one. 
    887943 
    888 >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': {'filename': 'test2.png', 'content': image_data}}, instance=instance) 
     944>>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}, instance=instance) 
    889945>>> f.is_valid() 
    890946True 
     
    893949u'...test2.png' 
    894950 
     951# Delete the current file since this is not done by Django. 
     952>>> os.unlink(instance.get_image_filename()) 
     953>>> instance.delete() 
     954 
     955>>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data)}) 
     956>>> f.is_valid() 
     957True 
     958>>> instance = f.save() 
     959>>> instance.image 
     960u'...test2.png' 
     961 
     962# Delete the current file since this is not done by Django. 
     963>>> os.unlink(instance.get_image_filename()) 
    895964>>> instance.delete() 
    896965 
     
    905974'' 
    906975 
    907 >>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': {'filename': 'test3.png', 'content': image_data}}, instance=instance) 
     976>>> f = ImageFileForm(data={'description': u'And a final one'}, files={'image': SimpleUploadedFile('test3.png', image_data)}, instance=instance) 
     977>>> f.is_valid()&nbs