Django + dropzone.js: form validation fails for multiple files - django

I'm trying to upload multiple files in one request using dropzone.js. When I set the uploadMultiple option to true in dropzone, one request containing both files is sent to my view, but form validation fails.
Here is my Django form:
class UploadForm(forms.Form):
data = forms.IntegerField(widget=forms.HiddenInput())
file = forms.FileField()
My view:
def upload(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
print request.FILES
if form.is_valid():
for file in request.FILES.getlist('file'):
print str(file)
else:
print form.errors
else:
form = UploadForm(initial={'data': 5})
return render(request, 'upload.html', {
'form': form
})
and my template:
<script type="text/javascript">
Dropzone.options.myAwesomeDropzone = {
autoProcessQueue : false,
uploadMultiple: true,
init : function() {
myDropzone = this;
this.element.querySelector("input[type='submit']").addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
myDropzone.processQueue();
});
}
}
</script>
<form id='my-awesome-dropzone' class="dropzone"
action="{% url 'upload.views.upload' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.data }}
<input type="submit" value="Upload" />
</form>
I see that request.FILES has two files:
<MultiValueDict: {u'file[]': [<InMemoryUploadedFile: Forest Flowers.jpg (image/jpeg)>,
<InMemoryUploadedFile: Forest.jpg (image/jpeg)>]}>
I guess the issue is Django doesn't recognize file[]. It expects file instead. How can I get Django to recognize the two uploads?

You are right assuming that the validation error is originated because of the input name dropzone.js sends to the server. The "file[n]" pattern when your Django form is expecting a field named "file" throws a validation error (required field).
In Dropzone.js you can specify the parameter "paramName" and this object property also accepts a function instead of a simple string, so if you set your paramName to something like:
...
paramName: function(){
return "file";
}
...
the name of the field sent to server doesn't change and you get a "file" field in request.FILES that is a dict with one element (file) that is an array of files as it is expected.

You just need to do:
file1 = request.FILES.get(file[][0], None) # For the first file
file2 = request.FILES.get(file[][1], None) # For the second file
... and so on...
Hope that helps.

Related

Django Search functionality: form returns None

i am trying to build a django search functionality for my app but the input form keeps returning a none
views.py
def search(request):
if request.method == 'POST':
query = request.POST.get('text')
houses = Product.objects.filter(name__contains='query')
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
search.html
<form>
<input type='text' placeholder='search houses>
<button type='submit'>Search</button>
</form>
First off, your python indentation is invalid, and your HTML is also invalid on the input line. I will assume this is a typo in the question, but if not, you have issues there.
Your main problem is the filter for houses:
houses = Product.objects.filter(name__contains='query')
is looking for a name containing the string "query". You need the variable you've just defined.
houses = Product.objects.filter(name__contains=query)
You have an indentation issue in the code you have posted.
You need to add action and method in your Form.
<form action="/url_of_search/" method="post">
Missing quote in input line.
<input type='text' placeholder='search houses'>
You need to use query instead of 'query' in the filter.
Product.objects.filter(name__contains=query)
Things missing in html code:
form action attribute
form method attribute
input field name attribute
<!-- add form attributes method and action -->
<form method="POST" action="{% url '<url_name>' %}">
<!-- add input attribute name to identify the field and pass the value in request body -->
<input type='text' placeholder='search houses' name='search_text'>
<button type='submit'>Search</button>
</form>
update views for search
def search(request):
if request.method == 'POST':
# use input field name to get the search text
query = request.POST.get('search_text')
houses = Product.objects.filter(name__contains=query)
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)

File Uploads via FormModel (Official description), but form.is_valid is always false

I'm trying to use FormModel to handle a file upload, but form.is_valid() is always returning false, and never shows any kind of error.Following is the code snippet from models.py,forms.py,views.py and my POST request.
models.py
class Pics(models.Model):
id = models.AutoField(primary_key=True,)
username = models.CharField(max_length=45)
path = models.ImageField(upload_to=img_path)
forms.py
class PicsForm(forms.ModelForm):
class Meta:
model = Pics
fields = ['username','path']
views.py
def uploads(request:HttpRequest):
form = PicsForm(request.POST,request.FILES)
if form.is_valid():
# instance = Pics(username=request.POST['username'],path=request.FILES['file'])
# instance.save()
form.save()
print('***')
else:
print('&&&&&&&&&&&&&')
return HttpResponse("succeed")
here is my postman set
I expect the output of '***', but the actual output is '&&&&&&&&&&&&&'
Be sure that your html form has 'enctype="multipart/form-data"' and your file upload input has name 'path', for example, your form should be like:
<form action="some_url" method="POST" enctype="multipart/form-data">
<input type='text' name='username'>
<input type="file" name="path">
</form>
I suspect Postman is not setting a CSRF token. Is there a reason that you are testing with Postman rather than creating an HTML form and submitting to it through your browser? I would do the latter, and if there is still a problem you could show us your template.

How can i return a Django ModelForm through an Jquery Ajax call?

Initially i am loading a modelform with an instance in the template end. But i have to change the instance upon some action using ajax and then refresh the form in the template end. Please suggest me is there any way to do that?
Normally, the server view uses a ModelForm to render a HTML snippet representing an instance of a Model (i.e. {{ myform.as_p }} or similar) that is then placed in a larger template representing an entire HTML document and finally sent to the client.
You need to create a second view that will render and return only the HTML snippet representing the form. From your original template you can then create an ajax call to your new view, which will return the forms HTML. You can replace your existing form with this via jQuery.
view:
def my_second_form(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
...
else:
form = MyForm()
return render(request, 'my_second_form.html', {
'form': form,
})
template:
<form action="/url/to/my_second_form/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
#djanog function.
def get_doctor_Data(request):
get_doctor = Doctor.objects.get(id=request.GET.get('dept_id'))
# Set initial value of django forms input and send as ajax respons
# If you getting any type of error so use form_name.as_p() method.
edit_form = AddDoctorForm(initial={'name':get_doctor.name, 'dep_id':get_doctor.id})
return HttpResponse(edit_form)
// Ajax method call.
$.ajax({
url: '/department/get_department_Data/',
type: 'GET',
data : {'dept_id': $(this).attr('id')},
success:(data)=>{
if(data){
// open modal
$('#basicModal').modal('show');
// Django forms get using ajax
$('#editForm').html(data)
}
}
})

Django: How to upload a file using ajax

I am using django 1.5, python 2.7 and jquery 1.9. I have a form which has precisely 2 fields i.e. title and document. When I press submit I want the users chosen document to be present in the request.FILES as shown in the view.
When I submit the regular form (without ajax), this works fine, but with ajax I do not get the file field in my request. Any suggestions on how to upload a file using ajax.
HTML:
<form enctype="multipart/form-data" action="{% url 'upload_document' %}" method="post" id="uploadForm">
{% csrf_token %}
<ul>
<li>
<div>Title</div>
<input id="title" type="text" maxlength="200"/>
<div class="error"></div>
</li>
<li>
<div>Upload File</div>
<input id="document" type="file" size="15" />
<div class="error"></div>
</li>
</ul>
<input type="submit" value="submit"/></p>
</form>
FORMS.PY:
class UploadForm( forms.Form ):
document = forms.FileField()
title = forms.CharField(max_length = 200)
def clean(self):
cleaned_data = super(UploadForm, self).clean()
return cleaned_data
def save(self, *args, **kwargs):
title = self.cleaned_data['title']
doc = self.cleaned_data['document']
document = Document(title = title, document = doc)
document.save()
return document
SCRIPT:
<script type="text/javascript">
$("#uploadForm").submit(function(event){
event.preventDefault();
$.ajax({
url : "{% url 'upload_document' %}",
type: "POST",
data : {csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
title: document.getElementById('title').value,
//document: document: document.getElementById('document'),
},
dataType : "json",
success: function( response ){
if(response == "True"){
// success
}
else {
//append errors
}
}
});
});
</script>
VIEWs.PY
def upload_document(request):
print request.POST
print request.FILES
if request.is_ajax():
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES, user = request.user)
if form.is_valid():
form.save()
return HttpResponse(simplejson.dumps('True'), mimetype = 'application/json' )
else:
errors = form.errors
return HttpResponse(simplejson.dumps(errors), mimetype = 'application/json' )
The answer to that question is not that simple. First of all if you intend to support old browsers then indeed it gets nasty. You have to deal with hidden iframes and some JavaScript tricks. I do advice using some well-known scripts for that like jQuery-File-Upload.
But the world is evolving and new technologies arise including HTML5. There's a new File API which is available in most modern browsers ( IE10+, FireFox3.6+, Chrome13+, see: http://caniuse.com/fileapi ) which can be used for that. First you need some HTML:
<input type="file" id="file-select" />
Then you can bind to (for example) change event:
$('#file-select').change( handleFileSelect );
and finally the handler itself:
var data = {};
function createReaderHandler(name) {
return function(ev) {
data[name] = ev.target.result;
};
}
function handleFileSelect(ev) {
var files = ev.target.files; // FileList object
// Loop through the FileList
for (var i = 0; i < files.length; i++) {
var file = files[i],
name = file.name || file.fileName,
reader = new FileReader();
reader.onload = createReaderHandler(name);
reader.readAsText(file);
}
}
Once the data is loaded into JavaScript memory (note that the operation is asynchronous) you can send it via AJAX like any other data. There are more options: depending on your file you can read it as a binary data using .readAsBinaryString and so on. Google is your friend. :)
Also I think there already are good scripts for uploading files with a fallback to old methods. This one can be interesting (haven't tried it):
http://www.plupload.com/
I think the issue is in the submit button, change it into normal button
ie, <button type='button' id='submit'>submit</button>(by default all buttons in form are submit)
and the ajax as
$('#submit').on('click',function(){
frm = $(this).parents('form')
$.ajax({
type: frm.attr('method'),
dataType:'json',
url: frm.attr('action'),
data: frm.serialize(),
async: false,
success: function (data) {
console.log('success')
},
error: function(data) {
console.log("Something went wrong!");
}
})
All others will be same
Just try it will work

success function in ajax in django

in views:
return render_to_response("main.html", RequestContext(request, {'form':form, "result":result}))
in template i have this jquery function:
$('#submitButton').click(function(e) {
e.preventDefault();
var dataPosted = $("#mainSubmit").serialize();
$.ajax({
type: "POST",
data: dataPosted,
url: 'main/',
success: function(data) {
$("#mainDiv").html(data);
$(".response").html({{ result }});
$(".response").show();
}
});
});
});
<div id="mainDiv" class="part">
<form id="mainSubmit" action="main/" method="POST" name="submitForm">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.as_p }}
<input type="submit" value="Submit" id="submitButton"/>
<div class="response" style="display: none;"></div>
</form>
</div>
but it seems that data can't be assigned to response div like this(it seems data is not defined). So i don't know what is the way to send data to template. if i use Httpresponse(result) in views, then i can't have my form refreshed, and only i can display in response div data i send from view. so what is the way?
EDIT:
This is my views. before, i didn't put else for the condition if form.is_valid(): , but here i use, because i think if i don't do this, it might cause some probables. i don't know what is the best way.
def mainFunc(request):
if request.is_ajax():
if request.method == 'POST':
form = mainForm(request.POST)
if form.is_valid():
// process the form
result = "successful"
to_json = {'form':form, 'result':result}
return HttpResponse(json.dumps(to_json), mimetype='application/json')
else:
result = ""
to_json = {'form':form, 'result':result}
return HttpResponse(json.dumps(to_json), mimetype='application/json')
else:
form = mainForm()
return render_to_response('main.html', RequestContext(request, {'form':form}))
else:
return render_to_response("ajax.html", {}, context_instance=RequestContext(request))
You need to return a response in format like JSON
You can use this snippet or more simple code like this:
from django.utils import simplejson
to_json = {'form':form, "result":result}
return HttpResponse(simplejson.dumps(to_json), mimetype='application/json')
Then you will be able to use data.result and data.form in your JS code.
If you use the same view for ajax and non-ajax call you can check for it with request.is_ajax()
Also you will not be able to use template tags and filters in your JS callback. So you need to pre-render your form before sending it via JSON
So the final code:
to_json = {'form':form, "result":result}
if request.is_ajax():
to_json['form'] = to_json['form'].as_p()
return HttpResponse(simplejson.dumps(to_json), mimetype='application/json')
else:
render_to_response("main.html", RequestContext(request, {'form':form, "result":result}))
Edit I assume that ajax.html is the template for the whole page and main.html is the template for mainDiv part of the page
So in is_ajax() part of you view you can return the data like this.
to_json = {}
to_json['form'] = render_to_string('main.html', {'form': form}, context_instance=RequestContext(request))
to_json['result'] = result
return HttpResponse(json.dumps(to_json), mimetype='application/json')
And you always return data like this, both for GET and POST AJAX calls
And in JS you get data like this:
$("#mainDiv").html(data.form);
$(".response").html(data.result);
$(".response").show();