Commit 868f805e authored by Okhin's avatar Okhin
Browse files

Fixing the multifile upload part

parent 0332e33d
...@@ -6,30 +6,23 @@ Released into the Public Domain ...@@ -6,30 +6,23 @@ Released into the Public Domain
""" """
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict, MergeDict
from django.utils.translation import ugettext from django.utils.translation import ugettext
from django.forms.fields import Field, EMPTY_VALUES from django.forms.fields import Field, EMPTY_VALUES
from django.core.files.uploadedfile import UploadedFile from django.core.files.uploadedfile import UploadedFile
from django.forms.widgets import FileInput from django.forms.widgets import Input, FILE_INPUT_CONTRADICTION
from django.forms.util import ErrorList, ValidationError, flatatt from django.forms.util import ErrorList, ValidationError, flatatt
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
class MultiFileInput(FileInput): FILE_INPUT_EMPTY_VALUE = object()
class MultiFileInput(Input):
""" """
A widget to be used by the MultiFileField to allow the user to upload A widget to be used by the MultiFileField to allow the user to upload
multiple files at one time. multiple files at one time.
""" """
input_type = 'file'
def __init__(self, attrs=None, *args, **kwargs): needs_multipart_form = True
"""
Create a MultiFileInput.
The 'count' attribute can be specified to default the number of
file boxes initially presented.
"""
super(MultiFileInput, self).__init__(attrs, *args, **kwargs)
self.attrs = {'count':1}
if attrs:
self.attrs.update(attrs)
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
""" """
...@@ -37,56 +30,23 @@ class MultiFileInput(FileInput): ...@@ -37,56 +30,23 @@ class MultiFileInput(FileInput):
Should not be overridden. Instead, subclasses should override the Should not be overridden. Instead, subclasses should override the
js, link, and/or fields methods which provide content to this method. js, link, and/or fields methods which provide content to this method.
""" """
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name+'[]') if attrs is None:
count = final_attrs['count'] attrs = {}
if count<1: count=1
del final_attrs['count']
js = self.js(name, value, count, final_attrs)
link = self.link(name, value, count, final_attrs)
fields = self.fields(name, value, count, final_attrs)
return mark_safe(js+link+fields)
def fields(self, name, value, count, attrs=None):
"""
Renders the necessary number of file input boxes.
"""
return u''.join([u'<input class="attachments"%s />\n' % flatatt(dict(attrs, id=attrs['id']+str(i))) for i in range(count)])
def link(self, name, value, count, attrs=None): name += '[]'
""" attrs['multiple'] = 'multiple'
Renders a link to add more file input boxes.
"""
return u"<div><a id='add_attach' onclick=\"javascript:new_%(name)s()\">Add more attachments</a></div>" % {'name':name}
def js(self, name, value, count, attrs=None): return super(MultiFileInput, self).render(name, None, attrs=attrs)
"""
Renders a bit of Javascript to add more file input boxes.
"""
return u"""
<script>
<!--
%(id)s_counter=%(count)d;
function new_%(name)s() {
b=document.getElementById('%(id)s0');
c=b.cloneNode(false);
c.id='%(id)s'+(%(id)s_counter++);
b.parentNode.insertBefore(c,b);
}
-->
</script>
""" % {'id':attrs['id'], 'name':name, 'count':count}
def value_from_datadict(self, data, files, name): def value_from_datadict(self, data, files, name):
""" """
File widgets take data from FILES, not POST. File widget takes data from FILES, not POST
we need to add [] for w3c recoomendation
""" """
name = name+'[]' name += '[]'
if isinstance(files, MultiValueDict): if isinstance(files, (MultiValueDict, MergeDict)):
return files.getlist(name) return files.getlist(name)
else: return files.get(name, None)
return None
def id_for_label(self, id_): def id_for_label(self, id_):
""" """
...@@ -102,56 +62,37 @@ class MultiFileField(Field): ...@@ -102,56 +62,37 @@ class MultiFileField(Field):
A field allowing users to upload multiple files at once. A field allowing users to upload multiple files at once.
""" """
widget = MultiFileInput widget = MultiFileInput
count = 1 max_length = None
allow_empty_file = None
def __init__(self, count=1, strict=False, *args, **kwargs): def to_python(self, data):
"""
strict is whether the number of files uploaded must equal count
"""
self.count = count
self.strict = strict
super(MultiFileField, self).__init__(*args, **kwargs)
def widget_attrs(self, widget):
"""
Adds the count to the MultiFileInput widget.
"""
if isinstance(widget, MultiFileInput):
return {'count':self.count}
return {}
def clean(self, data):
""" """
Cleans the data and makes sure that all the files had some content. Cleans the data and makes sure that all the files had some content.
Also checks whether a file was required. Also checks whether a file was required.
""" """
super(MultiFileField, self).clean(data) if data in EMPTY_VALUES:
if not self.required and data in EMPTY_VALUES:
return None return None
try: if data is FILE_INPUT_EMPTY_VALUE:
f = map(lambda a: UploadedFile(a['filename'], a['content']), data) raise validationError(self.Error_messages['empty_multiply'])
except TypeError:
raise ValidationError(ugettext(u"No file was submitted. Check the encoding type on the form.")) # UploadedFile objects should have name and size attributes
except KeyError: for d in data:
raise ValidationError(ugettext(u"No file was submitted.")) try:
file_name = d.name
for a_file in f: file_size = d.size
if not a_file.content: except AttributeError:
raise ValidationError(ugettext(u"The submitted file is empty.")) raise ValidationError(self.Error_messages['invalid'])
if self.strict and len(f) != self.count: if self.max_length is not None and file_size > self.max_length:
raise ValidationError(ugettext(u"An incorrect number of files were uploaded.")) error_values = {'max': self.max_length, 'length': file_size}
raise ValidationError(self.error_messages['max_length'] % error_values)
return f if not file_name:
raise ValidationError(self.error_messages['invalid'])
class FixedMultiFileInput(MultiFileInput): if not self.allow_empty_file and not file_size:
""" raise ValidationError(self.Error_messages['empty'])
A MultiFileInput widget that doesn't print the javascript code to allow return data
the user to add more file input boxes.
""" def bound_data(self, data, initial):
def link(self, name, value, count, attrs=None): if data in (None, FILE_INPUT_EMPTY_VALUE, FILE_INPUT_CONTRADICTION):
return u'' return initial
return data
def js(self, name, value, count, attrs=None):
return u''
...@@ -192,15 +192,6 @@ class AddForm(FormView): ...@@ -192,15 +192,6 @@ class AddForm(FormView):
messages.add_message(self.request, messages.INFO, _('Thank you for submitting this report, you will receive a verification email immediately, if not check your spam folder.')) messages.add_message(self.request, messages.INFO, _('Thank you for submitting this report, you will receive a verification email immediately, if not check your spam folder.'))
return super(AddForm, self).form_valid(form) return super(AddForm, self).form_valid(form)
def post(self, request, *args, **kwargs):
# We needto do special work with the form view
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect(self.success_url)
else:
return render(request, self.template_name, {'form': form})
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(AddForm, self).get_context_data(**kwargs) context = super(AddForm, self).get_context_data(**kwargs)
reports = sorted([(i['total'],i['id']) reports = sorted([(i['total'],i['id'])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment