Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.py[co]
*.egg-info
Empty file modified LICENSE
100644 → 100755
Empty file.
Empty file modified MANIFEST.in
100644 → 100755
Empty file.
58 changes: 26 additions & 32 deletions README.rst
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -49,46 +49,33 @@ Or, if using a ``ModelForm`` you can just override the widget.
Client Side
'''''''''''

Include the Javascript (and optionally CSS) files in your page and call the ``autoDiscover`` function.
This will search the page for all the AJAX file input fields and apply the necessary Javascript.
::

<link href="{{ STATIC_URL }}ajax_upload/css/ajax-upload-widget.css" rel="stylesheet" type="text/css"/>
<script src="{{ STATIC_URL }}ajax_upload/js/jquery.iframe-transport.js"></script>
<script src="{{ STATIC_URL }}ajax_upload/js/ajax-upload-widget.js"></script>
Just include ``{{ form.media }}`` line in your template for loading all js and css stuff.

<script>
$(function() {
AjaxUploadWidget.autoDiscover();
});
</script>


You can also pass options to ``autoDiscover()``:
JavaScript options
''''''''''''''''''
You can also pass some custom options to JavaScript ``AjaxUploadWidget`` object. For that you may use ``uploader_ops``
optional param:
::

<script>
$(function() {
AjaxUploadWidget.autoDiscover({
changeButtonText: 'Click to change',
onError: function(data) { alert('Error!'); }
// see source for full list of options
});
});
</script>
widgets = {
'my_image_field': AjaxClearableFileInput(uploader_ops={
'changeButtonText': "'Click to change'", # double quotes is required here
'onError': 'function(data) { alert('Error!'); }'
})
}


OR ... you can explicitly instantiate an AjaxUploadWidget on an AJAX file input field:
Using in django admin
'''''''''''''''''''''
An app is completely ready for using in django admin page. It's easy. See an example:
::

<input id="Foo" name="foo" type="file" data-upload-url="/ajax-upload/" data-filename="" data-required=""/>
<!-- The input field needs to be outputed by Django to contain the appropriate data attributes -->

<script>
new AjaxUploadWidget($('#Foo'), {
// options
});
</script>
class ProductAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ImageField: {'widget': AjaxClearableFileInput }
}


Dependencies
Expand All @@ -110,7 +97,14 @@ App Installation
(r'^ajax-upload/', include('ajax_upload.urls')),
)

1. That's it (don't forget include the Javascript as mentioned above).
1. That's it (don't forget include the jQuery as mentioned above).


Settings
--------

``AJAX_UPLOAD_MAX_FILESIZE`` - Maximum allowed file size in bytes (default: 0).
Setting this greater than 0 will enable validation of uploaded file size.


Running the Tests
Expand Down
Empty file modified ajax_upload/__init__.py
100644 → 100755
Empty file.
Empty file modified ajax_upload/admin.py
100644 → 100755
Empty file.
19 changes: 18 additions & 1 deletion ajax_upload/forms.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import uuid
import os

from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _

from ajax_upload.models import UploadedFile
from . import settings as upload_settings


class UploadedFileForm(forms.ModelForm):

if getattr(settings, 'AJAX_UPLOAD_USE_IMAGE_FIELD', False):
file = forms.ImageField()

ERRORS = {
'max_filesize': _('The file is bigger than the maximum size allowed.'),
}

class Meta:
model = UploadedFile
fields = ('file',)
Expand All @@ -15,5 +26,11 @@ def clean_file(self):
data = self.cleaned_data['file']
# Change the name of the file to something unguessable
# Construct the new name as <unique-hex>-<original>.<ext>
data.name = u'%s-%s' % (uuid.uuid4().hex, data.name)
original_name, ext = os.path.splitext(data.name)
data.name = u'%s-%s%s' % (uuid.uuid4().hex, original_name[:32], ext[:4])

max_upload_size = getattr(settings, 'AJAX_UPLOAD_MAX_FILESIZE', upload_settings.DEFAULT_MAX_FILESIZE)
if 0 < max_upload_size < data.size:
raise forms.ValidationError(self.ERRORS['max_filesize'])

return data
Empty file modified ajax_upload/models.py
100644 → 100755
Empty file.
4 changes: 4 additions & 0 deletions ajax_upload/settings.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from django.conf import settings


# Number of seconds to keep uploaded files. The clean_uploaded command will
# delete them after this has expired.
UPLOADER_DELETE_AFTER = getattr(settings, 'UPLOADER_DELETE_AFTER', 60 * 60)
DEFAULT_MAX_FILESIZE = 0
FILE_FIELD_MAX_LENGTH = getattr(settings, 'AJAX_UPLOAD_FILE_FIELD_MAX_LENGTH', 255)
Empty file modified ajax_upload/static/ajax_upload/css/ajax-upload-widget.css
100644 → 100755
Empty file.
13 changes: 13 additions & 0 deletions ajax_upload/static/ajax_upload/js/ajax-upload-admin-inline-hook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

// dirty fix for make uploader working in django admin inline
django.jQuery(function($){
var container = $('#content-main').delegate('.add-row', 'click', function(e){
container.find('.ajax-upload-mark').each(function(i, el){
var uploader_input = $(el);
if(uploader_input.attr('name').lastIndexOf('__prefix__') == -1){
uploader_input.removeClass('ajax-upload-mark');
new AjaxUploadWidget(uploader_input, uploader_input.data('uploader_options') || {});
}
});
});
});
2 changes: 1 addition & 1 deletion ajax_upload/static/ajax_upload/js/ajax-upload-widget.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(function() {
var global = this;
var $ = global.$;
var $ = global.$ || global.django.jQuery; // small trick for django admin
var console = global.console || {log: function() {}};

var AjaxUploadWidget = global.AjaxUploadWidget = function(element, options) {
Expand Down
2 changes: 1 addition & 1 deletion ajax_upload/static/ajax_upload/js/jquery.iframe-transport.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,4 @@
}
});

})(jQuery);
})(jQuery || django.jQuery); // small trick for django admin
19 changes: 19 additions & 0 deletions ajax_upload/templates/ajax_upload_widget.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{ input }}

<script type="text/javascript">

(jQuery || django.jQuery)(function($){
var uploader_input = $('#{{ id }}'),
options = {
{% for option, value in options.items %}
{{ option }}: {{ value|safe }}{% if not forloop.last %},{% endif %}
{% endfor %}
};
if(uploader_input.attr('name').lastIndexOf('__prefix__') == -1){
uploader_input.removeClass('ajax-upload-mark');
new AjaxUploadWidget(uploader_input, options);
} else { // small trick for making it work inside django admin inline
uploader_input.data('uploader_options', options);
}
});
</script>
Empty file.
13 changes: 13 additions & 0 deletions ajax_upload/templatetags/uploader_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.conf import settings
from django.template import Library

from ajax_upload import settings as upload_settings

register = Library()


@register.assignment_tag
def get_upload_settings():
return {
'max_filesize': getattr(settings, 'AJAX_UPLOAD_MAX_FILESIZE', upload_settings.DEFAULT_MAX_FILESIZE)
}
Empty file modified ajax_upload/tests/__init__.py
100644 → 100755
Empty file.
Empty file modified ajax_upload/tests/files/test.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified ajax_upload/tests/forms.py
100644 → 100755
Empty file.
25 changes: 25 additions & 0 deletions ajax_upload/tests/tests.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def create_uploaded_file(self, **kwargs):
return UploadedFile.objects.create(**defaults)


@override_settings(AJAX_UPLOAD_MAX_FILESIZE=0)
class AjaxUploadTests(UploaderTestHelper, TestCase):

def test_upload_file_submission_saves_file_with_different_name_and_returns_json_data(self):
Expand Down Expand Up @@ -59,7 +60,31 @@ def test_upload_file_get_request_returns_405(self):
response = self.client.get(reverse('ajax-upload'))
self.assertEqual(response.status_code, 405)

def test_upload_less_than_max_filesize_saves(self):
filesize = os.stat(TEST_FILEPATH).st_size
post_data = {
'file': open(TEST_FILEPATH),
}

with override_settings(AJAX_UPLOAD_MAX_FILESIZE=filesize):
response = self.client.post(reverse('ajax-upload'), post_data)
self.assertEqual(response.status_code, 200)

def test_upload_greater_than_max_filesize_returns_error(self):
filesize = os.stat(TEST_FILEPATH).st_size
post_data = {
'file': open(TEST_FILEPATH),
}

with override_settings(AJAX_UPLOAD_MAX_FILESIZE=filesize - 1):
response = self.client.post(reverse('ajax-upload'), post_data)
self.assertEqual(response.status_code, 400)
json_data = json.loads(response.content)
self.assertTrue('errors' in json_data)
self.assertEqual(json_data['errors']['file'][0], _('The file is bigger than the maximum size allowed.'))


@override_settings(AJAX_UPLOAD_MAX_FILESIZE=0)
class AjaxFileInputTests(UploaderTestHelper, TestCase):
urls = 'ajax_upload.tests.urls'

Expand Down
Empty file modified ajax_upload/tests/urls.py
100644 → 100755
Empty file.
Empty file modified ajax_upload/tests/views.py
100644 → 100755
Empty file.
Empty file modified ajax_upload/urls.py
100644 → 100755
Empty file.
Empty file modified ajax_upload/views.py
100644 → 100755
Empty file.
28 changes: 23 additions & 5 deletions ajax_upload/widgets.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import urllib2


from django import forms
from django.conf import settings
from django.core.files import File
from django.core.urlresolvers import reverse
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _

import urllib2

from ajax_upload.models import UploadedFile


Expand All @@ -15,23 +17,39 @@ class AjaxUploadException(Exception):


class AjaxClearableFileInput(forms.ClearableFileInput):

class Media:
js = ("ajax_upload/js/jquery.iframe-transport.js",
"ajax_upload/js/ajax-upload-widget.js",)
css = {'all': ('ajax_upload/css/ajax-upload-widget.css',)}

template_with_clear = '' # We don't need this
template_with_initial = '%(input)s'

def __init__(self, attrs=None, uploader_ops=None, template='ajax_upload_widget.html'):
super(AjaxClearableFileInput, self).__init__(attrs=attrs)
self.uploader_ops = uploader_ops or {}
self.template = template

def render(self, name, value, attrs=None):
attrs = attrs or {}
if value:
filename = u'%s%s' % (settings.MEDIA_URL, value)
else:
filename = ''

attrs.update({
'class': attrs.get('class', '') + 'ajax-upload',
'class': attrs.get('class', '') + ' ajax-upload ajax-upload-mark',
'data-filename': filename, # This is so the javascript can get the actual value
'data-required': self.is_required or '',
'data-upload-url': reverse('ajax-upload')
})
output = super(AjaxClearableFileInput, self).render(name, value, attrs)
return mark_safe(output)

return mark_safe(render_to_string(self.template, {
'input': super(AjaxClearableFileInput, self).render(name, value, attrs),
'id': self.build_attrs(attrs, type=self.input_type, name=name)['id'],
'options': self.uploader_ops
}))

def value_from_datadict(self, data, files, name):
# If a file was uploaded or the clear checkbox was checked, use that.
Expand Down
Empty file modified example/__init__.py
100644 → 100755
Empty file.
27 changes: 27 additions & 0 deletions example/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@


from django.contrib import admin
from django.db import models
from ajax_upload.widgets import AjaxClearableFileInput

from .models import Product, SubProduct


class SubProductInLine(admin.StackedInline):
model = SubProduct
formfield_overrides = {
models.ImageField: {'widget': AjaxClearableFileInput }
}
extra = 0

class Media:
js = ['ajax_upload/js/ajax-upload-admin-inline-hook.js']


class ProductAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ImageField: {'widget': AjaxClearableFileInput }
}
inlines = [SubProductInLine, ]

admin.site.register(Product, ProductAdmin)
Empty file modified example/forms.py
100644 → 100755
Empty file.
13 changes: 13 additions & 0 deletions example/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python
import os
import sys

#import pdb; pdb.set_trace()
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
10 changes: 10 additions & 0 deletions example/models.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@ class Product(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
image = models.ImageField(upload_to='examples/', blank=True)


class SubProduct(models.Model):
"""
Useless model just for inlines testing
"""
parent = models.ForeignKey(Product)
name = models.CharField(max_length=255)
description = models.TextField()
image = models.ImageField(upload_to='examples/', blank=True)
Loading