Ticket #13960: filetransfers.4.diff

File filetransfers.4.diff, 30.9 KB (added by Waldemar Kornewald, 14 years ago)

added admin support for file uploads and implemented request-based formsets

  • django/conf/global_settings.py

    diff -r c8038a5c808d django/conf/global_settings.py
    a b  
    258258# Default file storage mechanism that holds media.
    259259DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
    260260
     261# High-level transfer backends
     262FILE_TRANSFER_BACKENDS = (
     263    'django.core.files.transfers.DefaultFileBackend',
     264)
     265
     266# The default transfer backend's configuration mapping transfer_id to
     267# backend name
     268DEFAULT_FILE_TRANSFER_BACKENDS = {}
     269
    261270# Absolute path to the directory that holds media.
    262271# Example: "/home/media/media.lawrence.com/"
    263272MEDIA_ROOT = ''
  • django/contrib/admin/options.py

    diff -r c8038a5c808d django/contrib/admin/options.py
    a b  
    99from django.contrib import messages
    1010from django.views.decorators.csrf import csrf_protect
    1111from django.core.exceptions import PermissionDenied, ValidationError
     12from django.core.files.transfers import prepare_upload
    1213from django.db import models, transaction
    1314from django.db.models.fields import BLANK_CHOICE_DASH
    1415from django.http import Http404, HttpResponse, HttpResponseRedirect
     
    602603        """
    603604        formset.save()
    604605
    605     def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
     606    def has_file_field(self, form, formsets):
     607        for field in form.fields.values():
     608            if isinstance(field, forms.FileField):
     609                return True
     610        for formset in formsets:
     611            for form in formset.forms:
     612                for field in form.fields.values():
     613                    if isinstance(field, forms.FileField):
     614                        return True
     615        return False
     616
     617    def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None, has_file_field=True):
    606618        opts = self.model._meta
    607619        app_label = opts.app_label
    608620        ordered_objects = opts.get_ordered_objects()
     621
     622        if has_file_field:
     623            if not form_url:
     624                form_url = request.get_full_path()
     625            transfer_id = '%s.%s' % (self.__class__.__module__,
     626                                     self.__class__.__name__)
     627            if add:
     628                transfer_id += '.add'
     629            if change:
     630                transfer_id += '.change'
     631            form_url, upload_data = prepare_upload(transfer_id, request, url=form_url)
     632            context.update({
     633                'file_upload_data': mark_safe(''.join('<input type="hidden" name="%s" value="%s" />'
     634                                                      % (escape(name), escape(value))
     635                                                      for name, value in upload_data.items())),
     636            })
     637
    609638        context.update({
    610639            'add': add,
    611640            'change': change,
    612641            'has_add_permission': self.has_add_permission(request),
    613642            'has_change_permission': self.has_change_permission(request, obj),
    614643            'has_delete_permission': self.has_delete_permission(request, obj),
    615             'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
     644            'has_file_field': has_file_field,
    616645            'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
    617646            'ordered_objects': ordered_objects,
    618647            'form_url': mark_safe(form_url),
     
    822851                                  queryset=inline.queryset(request))
    823852                formsets.append(formset)
    824853
     854        has_file_field = self.has_file_field(form, formsets)
     855
    825856        adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
    826857            self.prepopulated_fields, self.get_readonly_fields(request),
    827858            model_admin=self)
     
    848879            'app_label': opts.app_label,
    849880        }
    850881        context.update(extra_context or {})
    851         return self.render_change_form(request, context, form_url=form_url, add=True)
     882        return self.render_change_form(request, context, form_url=form_url, add=True, has_file_field=has_file_field)
    852883
    853884    @csrf_protect_m
    854885    @transaction.commit_on_success
     
    913944                                  queryset=inline.queryset(request))
    914945                formsets.append(formset)
    915946
     947        has_file_field = self.has_file_field(form, formsets)
     948
    916949        adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj),
    917950            self.prepopulated_fields, self.get_readonly_fields(request, obj),
    918951            model_admin=self)
     
    940973            'app_label': opts.app_label,
    941974        }
    942975        context.update(extra_context or {})
    943         return self.render_change_form(request, context, change=True, obj=obj)
     976        return self.render_change_form(request, context, change=True, obj=obj, has_file_field=has_file_field)
    944977
    945978    @csrf_protect_m
    946979    def changelist_view(self, request, extra_context=None):
  • django/contrib/admin/templates/admin/change_form.html

    diff -r c8038a5c808d django/contrib/admin/templates/admin/change_form.html
    a b  
    4141    {{ adminform.form.non_field_errors }}
    4242{% endif %}
    4343
     44{{ file_upload_data }}
     45
    4446{% for fieldset in adminform %}
    4547  {% include "admin/includes/fieldset.html" %}
    4648{% endfor %}
  • django/contrib/admin/widgets.py

    diff -r c8038a5c808d django/contrib/admin/widgets.py
    a b  
    9494
    9595    def render(self, name, value, attrs=None):
    9696        output = []
    97         if value and hasattr(value, "url"):
     97        if value and hasattr(value, 'download_url'):
    9898            output.append('%s <a target="_blank" href="%s">%s</a> <br />%s ' % \
    99                 (_('Currently:'), value.url, value, _('Change:')))
     99                (_('Currently:'), value.download_url(), value, _('Change:')))
    100100        output.append(super(AdminFileWidget, self).render(name, value, attrs))
    101101        return mark_safe(u''.join(output))
    102102
  • new file django/core/files/transfers.py

    diff -r c8038a5c808d django/core/files/transfers.py
    - +  
     1from django.conf import settings
     2from django.core.exceptions import ImproperlyConfigured
     3from django.core.files.storage import default_storage
     4from django.http import HttpResponse
     5from django.utils.encoding import smart_str
     6from django.utils.importlib import import_module
     7import mimetypes
     8import urlparse
     9
     10class BaseFileBackend(object):
     11    def __init__(self, transfer_id):
     12        self.transfer_id = transfer_id
     13
     14    def prepare_upload(self, request, url):
     15        return None
     16
     17    def serve(self, request, file, save_as=False, content_type=None):
     18        return None
     19
     20    def download_url(self, file):
     21        return None
     22
     23    def get_storage_backend(self):
     24        return None
     25
     26class DefaultFileBackend(BaseFileBackend):
     27    def _get_backend_path(self):
     28        if self.transfer_id in settings.DEFAULT_FILE_TRANSFER_BACKENDS:
     29            return settings.DEFAULT_FILE_TRANSFER_BACKENDS[self.transfer_id]
     30        parts = self.transfer_id.split('.')[:-1]
     31        while parts:
     32            name = '.'.join(parts) + '.*'
     33            if name in settings.DEFAULT_FILE_TRANSFER_BACKENDS:
     34                return settings.DEFAULT_FILE_TRANSFER_BACKENDS[name]
     35            del parts[-1]
     36        return None
     37
     38    def _get_backend(self):
     39        path = self._get_backend_path()
     40        if path:
     41            return load_backend(path, self.transfer_id)
     42        return None
     43
     44    def prepare_upload(self, *args, **kwargs):
     45        backend = self._get_backend()
     46        if backend:
     47            return backend.prepare_upload(*args, **kwargs)
     48        return None
     49
     50    def serve(self, *args, **kwargs):
     51        backend = self._get_backend()
     52        if backend:
     53            return backend.serve(*args, **kwargs)
     54        return None
     55
     56    def download_url(self, *args, **kwargs):
     57        backend = self._get_backend()
     58        if backend:
     59            return backend.download_url(*args, **kwargs)
     60        return None
     61
     62    def get_storage_backend(self, *args, **kwargs):
     63        backend = self._get_backend()
     64        if backend:
     65            return backend.get_storage_backend(*args, **kwargs)
     66        return None
     67
     68# Public API
     69def prepare_upload(transfer_id, request, url=None):
     70    if not url:
     71        url = request.get_full_path()
     72    for backend in _get_backends(transfer_id):
     73        result = backend.prepare_upload(request, url)
     74        if result is not None:
     75            return result
     76
     77    # By default we simply return the URL unmodified
     78    return url, {}
     79
     80def serve_file(transfer_id, request, file, save_as=False, content_type=None):
     81    filename = file.name.rsplit('/')[-1]
     82    if save_as is True:
     83        save_as = filename
     84    if not content_type:
     85        content_type = mimetypes.guess_type(filename)[0]
     86    for backend in _get_backends(transfer_id):
     87        result = backend.serve(request, file, save_as=save_as, content_type=content_type)
     88        if result is not None:
     89            return result
     90
     91    return _default_serve_file(file, save_as, content_type)
     92
     93def download_url(transfer_id, file):
     94    for backend in _get_backends(transfer_id):
     95        result = backend.download_url(file)
     96        if result is not None:
     97            return result
     98
     99    # By default we use MEDIA_URL
     100    return urlparse.urljoin(settings.MEDIA_URL, file.name).replace('\\', '/')
     101
     102def get_storage_backend(transfer_id):
     103    for backend in _get_backends(transfer_id):
     104        result = backend.get_storage_backend()
     105        if result is not None:
     106            return result
     107
     108    # By default there is no public download URL
     109    return default_storage
     110
     111# Internal utilities
     112class ChunkedFile(object):
     113    def __init__(self, file):
     114        self.file = file
     115
     116    def __iter__(self):
     117        return self.file.chunks()
     118
     119def _default_serve_file(file, save_as, content_type):   
     120    """
     121    Serves the file in chunks for efficiency reasons.
     122   
     123    The transfer still goes through Django itself, so it's much worse than
     124    using the web server, but at least it works with all configurations.
     125    """
     126    response = HttpResponse(ChunkedFile(file), content_type=content_type)
     127    if save_as:
     128        response['Content-Disposition'] = smart_str(u'attachment; filename=%s' % save_as)
     129    if file.size is not None:
     130        response['Content-Length'] = file.size
     131    return response
     132
     133def _get_backends(*args, **kwargs):
     134    return (load_backend(name, *args, **kwargs)
     135            for name in settings.FILE_TRANSFER_BACKENDS)
     136
     137def load_backend(path, *args, **kwargs):
     138    module_name, attr_name = path.rsplit('.', 1)
     139    try:
     140        mod = import_module(module_name)
     141    except ImportError, e:
     142        raise ImproperlyConfigured('Error importing file backend module %s: "%s"' % (module_name, e))
     143    except ValueError, e:
     144        raise ImproperlyConfigured('Error importing file backend module. Is FILE_TRANSFER_BACKENDS a correctly defined list or tuple?')
     145    try:
     146        backend = getattr(mod, attr_name)
     147    except AttributeError:
     148        raise ImproperlyConfigured('Module "%s" does not define a "%s" file backend' % (module_name, attr_name))
     149    return backend(*args, **kwargs)
  • django/core/files/uploadhandler.py

    diff -r c8038a5c808d django/core/files/uploadhandler.py
    a b  
    8484        """
    8585        pass
    8686
    87     def new_file(self, field_name, file_name, content_type, content_length, charset=None):
     87    def new_file(self, field_name, file_name, content_type, content_length,  charset=None, content_type_extra=None):
    8888        """
    8989        Signal that a new file has been started.
    9090
     
    9696        self.content_type = content_type
    9797        self.content_length = content_length
    9898        self.charset = charset
     99        if content_type_extra is None:
     100            content_type_extra = {}
     101        self.content_type_extra = content_type_extra
    99102
    100103    def receive_data_chunk(self, raw_data, start):
    101104        """
  • django/db/models/fields/files.py

    diff -r c8038a5c808d django/db/models/fields/files.py
    a b  
    66from django.conf import settings
    77from django.db.models.fields import Field
    88from django.core.files.base import File, ContentFile
    9 from django.core.files.storage import default_storage
    109from django.core.files.images import ImageFile, get_image_dimensions
     10from django.core.files.transfers import serve_file, download_url, get_storage_backend
    1111from django.core.files.uploadedfile import UploadedFile
    1212from django.utils.functional import curry
    1313from django.db.models import signals
     
    139139        # be restored later, by FileDescriptor below.
    140140        return {'name': self.name, 'closed': False, '_committed': True, '_file': None}
    141141
     142    def serve(self, request, save_as=False, content_type=None):
     143        self._require_file()
     144        return serve_file(self.field.transfer_id, request, self, save_as=save_as, content_type=content_type)
     145
     146    def download_url(self):
     147        self._require_file()
     148        return download_url(self.field.transfer_id, self)
     149
    142150class FileDescriptor(object):
    143151    """
    144152    The descriptor for the file attribute on the model instance. Returns a
     
    219227
    220228    description = ugettext_lazy("File path")
    221229
    222     def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
     230    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, transfer_id=None, **kwargs):
    223231        for arg in ('primary_key', 'unique'):
    224232            if arg in kwargs:
    225233                raise TypeError("'%s' is not a valid argument for %s." % (arg, self.__class__))
    226234
    227         self.storage = storage or default_storage
     235        self.transfer_id = transfer_id
     236        self.storage = storage
    228237        self.upload_to = upload_to
    229238        if callable(upload_to):
    230239            self.generate_filename = upload_to
     
    257266
    258267    def contribute_to_class(self, cls, name):
    259268        super(FileField, self).contribute_to_class(cls, name)
     269        if self.transfer_id is None:
     270            self.transfer_id = '%s.%s.%s' % (cls.__module__,
     271                                             cls.__name__,
     272                                             self.name)
     273        if self.storage is None:
     274            self.storage = get_storage_backend(self.transfer_id)
    260275        setattr(cls, self.name, self.descriptor_class(self))
    261276        signals.post_delete.connect(self.delete_file, sender=cls)
    262277
  • django/forms/forms.py

    diff -r c8038a5c808d django/forms/forms.py
    a b  
    33"""
    44
    55from django.core.exceptions import ValidationError
     6from django.core.files.transfers import prepare_upload
    67from django.utils.copycompat import deepcopy
    78from django.utils.datastructures import SortedDict
    89from django.utils.html import conditional_escape
     
    1011from django.utils.safestring import mark_safe
    1112
    1213from fields import Field, FileField
    13 from widgets import Media, media_property, TextInput, Textarea
     14from widgets import Media, media_property, TextInput, Textarea, HiddenInput
    1415from util import flatatt, ErrorDict, ErrorList
    1516
    16 __all__ = ('BaseForm', 'Form')
     17__all__ = ('BaseForm', 'Form', 'RequestForm')
    1718
    1819NON_FIELD_ERRORS = '__all__'
    1920
     
    377378        """
    378379        return [field for field in self if not field.is_hidden]
    379380
     381class BaseRequestFormTraits(object):
     382    def __init__(self, request, url=None, prepare_upload=True, transfer_id=None, **kwargs):
     383        if request.method == 'POST':
     384            kwargs.setdefault('data', request.POST)
     385            kwargs.setdefault('files', request.FILES)
     386
     387        self.request = request
     388
     389        # _url is the original url
     390        self._url = url or request.get_full_path()
     391        if not transfer_id:
     392            self.transfer_id = '%s.%s' % (self.__class__.__module__,
     393                                          self.__class__.__name__)
     394        self._file_upload_data = {}
     395        super(BaseRequestFormTraits, self).__init__(**kwargs)
     396        if prepare_upload:
     397            self.prepare_upload()
     398
     399    def prepare_upload(self):
     400        if self.files:
     401            return
     402
     403        self._remove_upload_data()
     404
     405        # Only prepare upload if a FileField exists
     406        if not self._has_file_field():
     407            return
     408
     409        result = prepare_upload(self.transfer_id, self.request, url=self._url)
     410        self.url, self._file_upload_data = result
     411
     412        self._add_upload_data()
     413
     414    def _has_file_field(self):
     415        for field in self.fields.values():
     416            if isinstance(field, FileField):
     417                return True
     418        return False
     419
     420    def _remove_upload_data(self):
     421        for name in self._file_upload_data:
     422            del self.fields[name]
     423
     424    def _add_upload_data(self):
     425        for name, value in self._file_upload_data.items():
     426            self.fields[name] = Field(initial=value, required=False, widget=HiddenInput)
     427
    380428class Form(BaseForm):
    381429    "A collection of Fields, plus their associated data."
    382430    # This is a separate class from BaseForm in order to abstract the way
     
    386434    # BaseForm itself has no way of designating self.fields.
    387435    __metaclass__ = DeclarativeFieldsMetaclass
    388436
     437class RequestForm(BaseRequestFormTraits, BaseForm):
     438    __metaclass__ = DeclarativeFieldsMetaclass
     439
    389440class BoundField(StrAndUnicode):
    390441    "A Field plus data"
    391442    def __init__(self, form, field, name):
  • django/forms/formsets.py

    diff -r c8038a5c808d django/forms/formsets.py
    a b  
    1 from forms import Form
     1from forms import Form, BaseRequestFormTraits, RequestForm
    22from django.core.exceptions import ValidationError
    33from django.utils.encoding import StrAndUnicode
    44from django.utils.safestring import mark_safe
    55from django.utils.translation import ugettext as _
    6 from fields import IntegerField, BooleanField
     6from fields import Field, IntegerField, BooleanField, FileField
    77from widgets import Media, HiddenInput
    88from util import ErrorList
    99
    10 __all__ = ('BaseFormSet', 'all_valid')
     10__all__ = ('BaseFormSet', 'BaseRequestFormSet', 'all_valid')
    1111
    1212# special field names
    1313TOTAL_FORM_COUNT = 'TOTAL_FORMS'
     
    326326        forms = u' '.join([form.as_table() for form in self.forms])
    327327        return mark_safe(u'\n'.join([unicode(self.management_form), forms]))
    328328
     329class BaseRequestFormSetTraits(BaseRequestFormTraits):
     330    def _construct_form(self, *args, **kwargs):
     331        if isinstance(self.form, BaseRequestFormTraits):
     332            kwargs.update({
     333                'request': self.request,
     334                'url': self._url,
     335                'prepare_upload': False,
     336            })
     337        super(BaseRequestFormSetTraits, self)._construct_form(*args, **kwargs)
     338
     339    def _get_empty_form(self, *args, **kwargs):
     340        if isinstance(self.form, BaseRequestFormTraits):
     341            kwargs.update({
     342                'request': self.request,
     343                'url': self._url,
     344                'prepare_upload': False,
     345            })
     346        super(BaseRequestFormSetTraits, self)._get_empty_form(*args, **kwargs)
     347    empty_form = property(_get_empty_form)
     348
     349    def _has_file_field(self):
     350        for form in self.forms:
     351            for field in form.fields.values():
     352                if isinstance(field, FileField):
     353                    return True
     354        return False
     355
     356    def _remove_upload_data(self):
     357        if hasattr(self, '_upload_data_form'):
     358            self.forms.remove(self._upload_data_form)
     359
     360    def _add_upload_data(self):
     361        form = RequestForm(self.request)
     362        for name, value in self._file_upload_data.items():
     363            form.fields[name] = Field(initial=value, required=False, widget=HiddenInput)
     364        self._upload_data_form = form
     365        self.forms.append(form)   
     366
     367class BaseRequestFormSet(BaseRequestFormSetTraits, BaseFormSet):
     368    pass
     369
    329370def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
    330371                    can_delete=False, max_num=None):
    331372    """Return a FormSet for the given form class."""
  • django/forms/models.py

    diff -r c8038a5c808d django/forms/models.py
    a b  
    1212from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
    1313from django.core.validators import EMPTY_VALUES
    1414from util import ErrorList
    15 from forms import BaseForm, get_declared_fields
     15from forms import BaseForm, BaseRequestFormTraits, get_declared_fields
    1616from fields import Field, ChoiceField
    1717from widgets import SelectMultiple, HiddenInput, MultipleHiddenInput
    1818from widgets import media_property
    19 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
     19from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME, BaseRequestFormSetTraits
    2020
    2121__all__ = (
    2222    'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
    2323    'save_instance', 'form_for_fields', 'ModelChoiceField',
    24     'ModelMultipleChoiceField',
     24    'ModelMultipleChoiceField', 'RequestModelForm', 'BaseRequestModelFormSet',
     25    'BaseInlineRequestFormSet',
    2526)
    2627
    2728def construct_instance(form, instance, fields=None, exclude=None):
     
    201202        formfield_callback = attrs.pop('formfield_callback',
    202203                lambda f, **kwargs: f.formfield(**kwargs))
    203204        try:
    204             parents = [b for b in bases if issubclass(b, ModelForm)]
     205            parents = [b for b in bases if issubclass(b, (BaseModelForm))]
    205206        except NameError:
    206207            # We are defining ModelForm itself.
    207208            parents = None
     
    375376class ModelForm(BaseModelForm):
    376377    __metaclass__ = ModelFormMetaclass
    377378
     379class RequestModelForm(BaseRequestFormTraits, BaseModelForm):
     380    __metaclass__ = ModelFormMetaclass
     381
    378382def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
    379383                       formfield_callback=lambda f: f.formfield()):
    380384    # Create the inner Meta class. FIXME: ideally, we should be able to
     
    658662            form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
    659663        super(BaseModelFormSet, self).add_fields(form, index)
    660664
     665class BaseRequestModelFormSet(BaseRequestFormSetTraits, BaseModelFormSet):
     666    pass
     667
    661668def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
    662669                         formset=BaseModelFormSet,
    663670                         extra=1, can_delete=False, can_order=False,
     
    768775        unique_check = [field for field in unique_check if field != self.fk.name]
    769776        return super(BaseInlineFormSet, self).get_unique_error_message(unique_check)
    770777
     778class BaseInlineRequestFormSet(BaseRequestFormSetTraits, BaseInlineFormSet):
     779    pass
    771780
    772781def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
    773782    """
  • django/http/multipartparser.py

    diff -r c8038a5c808d django/http/multipartparser.py
    a b  
    169169                    file_name = self.IE_sanitize(unescape_entities(file_name))
    170170
    171171                    content_type = meta_data.get('content-type', ('',))[0].strip()
     172                    content_type_extra = meta_data.get('content-type', (0,{}))[1]
    172173                    try:
    173                         charset = meta_data.get('content-type', (0,{}))[1].get('charset', None)
     174                        charset = content_type_extra.get('charset', None)
    174175                    except:
    175176                        charset = None
    176177
     
    185186                            try:
    186187                                handler.new_file(field_name, file_name,
    187188                                                 content_type, content_length,
    188                                                  charset)
     189                                                 charset, content_type_extra)
    189190                            except StopFutureHandlers:
    190191                                break
    191192
  • new file tests/regressiontests/file_uploads/backends.py

    diff -r c8038a5c808d tests/regressiontests/file_uploads/backends.py
    - +  
     1from django.conf import settings
     2from django.core.files.transfers import BaseFileBackend
     3from django.http import HttpResponseRedirect
     4import models
     5
     6class TestFileBackend(BaseFileBackend):
     7    def prepare_upload(self, request, url, *args, **kwargs):
     8        return '/redirect' + url, {'x-extra': 'abcd', 'x-data': 'efg'}
     9
     10    def serve(self, request, file, *args, **kwargs):
     11        if not file.name.endswith('pass'):
     12            return HttpResponseRedirect('/download/')
     13
     14    def download_url(self, file):
     15        if not file.name.endswith('pass'):
     16            return '/public/'
     17
     18    def get_storage_backend(self, *args, **kwargs):
     19        return models.temp_storage
  • new file tests/regressiontests/file_uploads/forms.py

    diff -r c8038a5c808d tests/regressiontests/file_uploads/forms.py
    - +  
     1from django import forms
     2from models import FileModel
     3
     4class FileForm(forms.RequestModelForm):
     5    class Meta:
     6        model = FileModel
  • tests/regressiontests/file_uploads/models.py

    diff -r c8038a5c808d tests/regressiontests/file_uploads/models.py
    a b  
    11import tempfile
    22import os
     3from django.conf import settings
    34from django.db import models
    45from django.core.files.storage import FileSystemStorage
    56
    67temp_storage = FileSystemStorage(tempfile.mkdtemp())
    78UPLOAD_TO = os.path.join(temp_storage.location, 'test_upload')
     9settings.DEFAULT_FILE_TRANSFER_BACKENDS.update({
     10    'regressiontests.file_uploads.*': 'regressiontests.file_uploads.backends.TestFileBackend',
     11})
    812
    913class FileModel(models.Model):
    10     testfile = models.FileField(storage=temp_storage, upload_to='test_upload')
     14    testfile = models.FileField(upload_to='test_upload')
  • tests/regressiontests/file_uploads/tests.py

    diff -r c8038a5c808d tests/regressiontests/file_uploads/tests.py
    a b  
    55import unittest
    66from StringIO import StringIO
    77
     8from django.conf import settings
    89from django.core.files import temp as tempfile
    910from django.core.files.uploadedfile import SimpleUploadedFile
    1011from django.test import TestCase, client
     
    1718
    1819UNICODE_FILENAME = u'test-0123456789_中文_Orléans.jpg'
    1920
     21class FileBackendTests(TestCase):
     22    def setUp(self):
     23        if not os.path.isdir(temp_storage.location):
     24            os.makedirs(temp_storage.location)
     25        if os.path.isdir(UPLOAD_TO):
     26            shutil.rmtree(UPLOAD_TO)
     27
     28    def tearDown(self):
     29        shutil.rmtree(temp_storage.location)
     30
     31    def test_prepare_upload(self):
     32        response = self.client.get('/file_uploads/prepare_upload/')
     33        self.assertEqual(response.status_code, 200)
     34        data = simplejson.loads(response.content)
     35        self.assertEqual(data, [
     36            '/redirect/file_uploads/prepare_upload/', [
     37                '<input type="file" name="testfile" id="id_testfile" />',
     38                '<input type="hidden" name="x-data" value="efg" id="id_x-data" />',
     39                '<input type="hidden" name="x-extra" value="abcd" id="id_x-extra" />',
     40            ]
     41        ])
     42
     43    def test_serve_file(self):
     44        obj = FileModel()
     45        obj.testfile.save('foo.txt', SimpleUploadedFile('foo.txt', 'x'))
     46        obj.save()
     47        response = self.client.get('/file_uploads/serve/')
     48        self.assertEqual(response.status_code, 302)
     49        self.assertEqual(response['Location'], 'http://testserver/download/')
     50
     51    def test_serve_file_fallback(self):
     52        obj = FileModel()
     53        obj.testfile.save('foo.pass', SimpleUploadedFile('foo.pass', 'x'))
     54        obj.save()
     55        response = self.client.get('/file_uploads/serve/')
     56        self.assertEqual(response.status_code, 200)
     57
     58    def test_download_url(self):
     59        obj = FileModel()
     60        obj.testfile.save('foo2.txt', SimpleUploadedFile('foo2.txt', 'x'))
     61        obj.save()
     62        url = obj.testfile.download_url()
     63        self.assertEqual(url, '/public/')
     64
     65    def test_download_url_fallback(self):
     66        obj = FileModel()
     67        obj.testfile.save('foo2.pass', SimpleUploadedFile('foo2.pass', 'x'))
     68        obj.save()
     69        url = obj.testfile.download_url()
     70        self.assertEqual(url, settings.MEDIA_URL + obj.testfile.name)
     71
    2072class FileUploadTests(TestCase):
    2173    def test_simple_upload(self):
    2274        post_data = {
     
    122174        response = self.client.request(**r)
    123175
    124176        # The filenames should have been sanitized by the time it got to the view.
    125         recieved = simplejson.loads(response.content)
     177        received = simplejson.loads(response.content)
    126178        for i, name in enumerate(scary_file_names):
    127             got = recieved["file%s" % i]
     179            got = received["file%s" % i]
    128180            self.assertEqual(got, "hax0rd.txt")
    129181
    130182    def test_filename_overflow(self):
  • tests/regressiontests/file_uploads/urls.py

    diff -r c8038a5c808d tests/regressiontests/file_uploads/urls.py
    a b  
    1010    (r'^quota/broken/$',    views.file_upload_quota_broken),
    1111    (r'^getlist_count/$',   views.file_upload_getlist_count),
    1212    (r'^upload_errors/$',   views.file_upload_errors),
     13    (r'^prepare_upload/$',  views.file_prepare_upload),
     14    (r'^serve/$', views.file_serve),
    1315)
  • tests/regressiontests/file_uploads/views.py

    diff -r c8038a5c808d tests/regressiontests/file_uploads/views.py
    a b  
    22from django.core.files.uploadedfile import UploadedFile
    33from django.http import HttpResponse, HttpResponseServerError
    44from django.utils import simplejson
     5from forms import FileForm
    56from models import FileModel, UPLOAD_TO
    67from uploadhandler import QuotaUploadHandler, ErroringUploadHandler
    78from django.utils.hashcompat import sha_constructor
     
    112113def file_upload_errors(request):
    113114    request.upload_handlers.insert(0, ErroringUploadHandler())
    114115    return file_upload_echo(request)
     116
     117def file_prepare_upload(request):
     118    form = FileForm(request)
     119    return HttpResponse(simplejson.dumps([form.url, list(map(unicode, form))]))
     120
     121def file_serve(request):
     122    return FileModel.objects.all()[0].testfile.serve(request)
Back to Top