Why ajax put FormData into request.data instead of request.files? - ajax

Flask, and I want to upload an image file through an AJAX call in HTML.
I wrote this Javascript:
function mysubmit(input){
var file_input = document.getElementById('file-input-left');
var form_data = new FormData();
var file = file_input.files[0];
alert(file);
form_data.append('file', file);
$.ajax({
type: 'POST',
url: '/calc_full_result',
data: form_data,
contentType: false,
cache: false,
processData: false,
async: false,
success: function(data){
alert('Success!');
},
});
}
And the HTML:
<div class="image-holder">
<form action="/calc_full_result" id="upload-form-left" name="form-left" method=post enctype=multipart/form-data>
<div class="image-holder-left">
<div class="image-upload">
{% if img_left %}
<label id="upload-left-label" for="file-input-left"><img id="left-image" src="data:image/png;base64, {{img_left}}"/></label>
{% else %}
<label id="upload-left-label" for="file-input-left"><img id="left-image" src="{{url_for('static', filename='upload_fill.png')}}" /></label>
{% endif %}
<input id="file-input-left" type=file name=file onchange="mysubmit(this)"/>
</div>
</div>
<form action="/calc_full_result" id="upload-form-right" name="form-right" method=post enctype=multipart/form-data>
<div class="image-holder-right">
<div class="image-upload">
{% if img_right %}
<label id="upload-right-label" for="file-input-right"><img id="right-image" src="data:image/png;base64, {{img_right}}"/></label>
{% else %}
<label id="upload-right-label" for="file-input-right"><img id="right-image" src="{{url_for('static', filename='upload_fill.png')}}"/></label>
{% endif %}
<input id="file-input-right" type=file name=file onchange="mysubmit(this)"/>
</div>
</div>
</form>
</div>
Then in app.py:
#app.route('/calc_full_result', methods=['POST'])
def calc_full_result():
if request.method == 'POST':
file = request.files['file']
return jsonify(result="suc")
But I cannot get the object file, it's always empty and cannot be saved. When I check the request I can see len(request.data) is almost the size of image file.
I wonder why request.files is empty and how to save the file in request.data? Is it possible?

Try:
data: new FormData(document.getElementById('upload-form-left'))

Related

Django Python can not upload directory with large quantity of files

I am trying to upload a directory with large number of dicom files using Django and JQuery ajax. Each file size is not more than 600kb. I can upload 200 files at a time. If I increase the number of files (tried to upload 14000 files), it doesn’t work. The site gets stuck and it’s not showing any error. Can anyone please help me with this problem? I have attached my code below. Thanks in advance.
View.py:
def handle_uploaded_file(f,filePath):
with open(filePath+'/'+f.name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
def UploadScanView(request):
if request.method == 'POST':
form = create_scan_form(request.POST)
if form.is_valid():
scan = form.save(commit=False)
scan.project_id = form.cleaned_data.get('project_id')
directory_name = request.POST.get('directories')
json_to_dictionary = json.loads(directory_name)
print(json_to_dictionary)
for upload_file in request.FILES.getlist('file'):
file_path = settings.MEDIA_ROOT+'/'+os.path.dirname(json_to_dictionary[upload_file.name])
print(file_path)
if os.path.exists(file_path):
handle_uploaded_file(upload_file, file_path)
else:
os.makedirs(file_path)
handle_uploaded_file(upload_file,file_path)
return render(request, 'fileupload/basic_upload/scan_upload.html', {'form': form})
else:
form = create_scan_form()
return render(request, 'fileupload/basic_upload/scan_upload.html', {'form': form})
HTML and JQuery:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script>
/*fileupload div*/
$(document).ready(function(){
$("#my-file").on('change',function(e){ //submit(function(e){
$("#file-wrap p").text('Now click on Upload button');
});
$("#my-form").on('submit',function(e){ //submit(function(e){
files = document.querySelector("#my-file").files;
var directories = {}
for (var file of files) {
file.webkitRelativePath
directories[file.name] = file.webkitRelativePath
}
directories = JSON.stringify(directories);
document.querySelector("#directories").value = directories
var eventType = $(this).attr("method"); // get method type for #my-form
var eventLink = $(this).attr("action"); // get action link for #my-form
//alert(directories);
//////
var formData = new FormData(this);
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}');
$.ajax({
headers: { "X-CSRFToken": '{{ csrf_token }}' },
type: eventType,
url: eventLink,
//data: new FormData(this), // IMPORTANt
data: formData,
cache: false,
contentType: false,
processData: false,
// this part is progress bar
xhr: function () {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function (e) {
if (e.lengthComputable) {
var percentComplete = e.loaded / e.total;
percentComplete = parseInt(percentComplete * 100);
$('.myprogress').text(percentComplete + '%');
$('.myprogress').css('width', percentComplete + '%');
}
}, false);
return xhr;
},
success: function(getResult) {
$('#my-form')[0].reset(); // reset form
$("#file-wrap p").html('Drag and drop file here'); // change wrap message
}
});
e.preventDefault();
});
});
/*fileupload div*/
</script>
<form id="my-form" method="POST" action="{% url 'fileupload:upload_scan' %}" enctype="multipart/form-data"> <!--independentSub-->
{% csrf_token %}
<div id="user_form" class="container">
<div class="form-group col-sm-4" id="project_id">
{{ form.project_id|as_crispy_field }}
</div>
<!--fileupload div-->
<div id="independentSubDiv" class="row">
<div id="file-wrap" class="form-group col-sm-6" >
<p>Drag and drop file here</p>
<input id="my-file" type="file" name="file" multiple webkitdirectory directory draggable="true">
<input type="text" id="directories" name="directories" hidden />
</div>
</div>
<div style="padding-left: initial" id="independentSubDiv" class="form-group col-sm-7" >
<button type="submit" class="btn btn-primary btn-lg btn-block" name="submit_btn" id="submit_btn">Submit</button>
</div>
<div class="progress form-group col-sm-7" style="padding-left: initial" id="progressDiv" >
<div class="progress-bar progress-bar-success myprogress " role="progressbar">0%</div>
</div>
</div>
</form>

Using Ajax and Django conditions to display an html variable

I'm new to using Ajax calls with Django. Im trying to implement a simple Display or not display depending on the type of get called by Ajax:
Views:
def dynamic(request):
context = {}
if request.method == 'GET':
if 'backtest_type' in request.GET:
context['display_details'] = False
print ('Im not displaying')
elif 'change_val' in request.GET:
context['display_details'] = True
print ('Im displaying')
else:
context['display_details'] = False
return render(request, "demo/dynamic.html", context)
In my template:
{% extends 'demo/base.html' %}
{% block body %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'css/dynamic.css' %}">
<script>
$(document).on('click','#id_backtest_type', function () {
console.log( $(this).val() );
$.ajax({
type: 'get',
data: {'backtest_type': 1},
success: function (data) {
console.log('data sent ' + data);
},
failure: function(data) {
alert('Got an error dude');
}
});
});
$(document).on('click','#btnClick', function () {
// console.log( $(this).val() );
$.ajax({
type: 'get',
data: {'change_val': 1},
success: function (data) {
console.log('data sent ' + data);
failure: function(data) {
alert('Got an error dude');
}
});
});
</script>
<div>
{% if display_details %}
<h1>I'm diplaying</h1>
{% endif %}
</div>
<div id="btnClick" class="col-sm-4">
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
<button class="btn btn-secondary" type="button">Don't Display</button>
</div>
</div>
</div>
<div id="id_backtest_type" class="col-sm-4">
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
<button class="btn btn-secondary" type="button">Display!</button>
</div>
</div>
</div>
{% endblock %}
From the console I'm sure that the get requests are being correctly sent using ajax (the get requests are being sent and the console prints the statements:
[09/Feb/2019 20:00:56] "GET /dynamic/?backtest_type=1 HTTP/1.1" 200
6530
Im displaying
However, even if the ajax call works correctly, the template doesn't end up rendering <h1>I'm diplaying</h1>. Am I misunderstanding anything about Ajax?
Thanks in advance!

Django: Uploading files with AJAX: Form says that the file input field is empty (or CSRF token missing or incorrect)

I'm trying to add a very simple file upload modal form in my Django app.
But, when I click the submit button, the form shows me an error message: "this field is required".
Everything renders correctly:
My main page loads correctly
When I click in the "Agregar archivo adjunto" button ("add attachment"), the modal form shows correctly, and all the fields are rendered as I want them to.
The issue comes when I click on the "Adjuntar archivo" ("attach file") button in my modal field: The form throws an error, as if I was trying to upload a "null" file!
Now... I forgot to add cache: false, contentType: false, processData: false to the $.ajax() call, but, when I add them, I get the following error: Forbidden (CSRF token missing or incorrect.). So... I don't know how to proceed!
I've already wrote (successfully) a modal form which helps me add notes (related) to my lead object (using this reference), and I'm trying to reproduce exactly the same process for a file upload modal dialog... but it doesn't work :(
Any help will be really appreciated.
By the way: I'm testing this using Chrome (no IE will be used!)
Here is my code:
models.py
def lead_dir_path(instance, filename):
"""
Files will be saved to: MEDIA_ROOT/leads/<int:pk>/<filename>
where <int:pk> is lead's primary key, and <filename> is just that.
Filename will be set to an UUID value.
"""
ext = filename.split('.')[-1]
filename = '%s.%s' % (uuid.uuid4(), ext)
return 'leads/%s/%s' % (instance.lead.pk, filename)
class ArchivosAdjuntosLead(models.Model):
lead = models.ForeignKey(Lead, on_delete=models.CASCADE)
descripcion = models.CharField(max_length=100)
archivo = models.FileField(upload_to=lead_dir_path)
views.py
def agregar_adjunto_json(request, pk):
"""
Adds a file to lead with id=pk
"""
context = {}
data = {}
lead = get_object_or_404(Lead, pk=pk)
context['lead'] = lead
if request.method == 'POST':
form = AdjuntarArchivoLeadForm_v2(request.POST, request.FILES)
if form.is_valid():
form.save();
data['form_is_valid'] = True
else:
data['form_is_valid'] = False
else:
form = AdjuntarArchivoLeadForm_v2()
form.initial = {'lead': lead}
context['form'] = form
data['html_form'] = render_to_string(
template_folder + 'partial_templates/partial_adjuntar_archivo.html',
context,
request = request,
)
return JsonResponse(data)
forms.py
class AdjuntarArchivoLeadForm_v2(forms.ModelForm):
class Meta():
model = ArchivosAdjuntosLead
fields = ['lead', 'descripcion', 'archivo']
widgets = {
'lead': forms.TextInput(attrs={'class':'form-control', 'style':'display:none;'}),
'descripcion': forms.TextInput(attrs={'class':'form-control'}),
'archivo': forms.FileInput(attrs={'class':'form-control'}),
}
partial_adjuntar_archivo.html
I use this partial template to create a modal form:
<form method="POST" enctype="multipart/form-data"
action="{% url 'leads:agregar_adjunto_v2' pk=lead.pk %}"
id="js_adjuntar_archivo_form">
{% csrf_token %}
<div class="modal-header">
<h4 class="modal-title">Adjuntar archivo</h4>
</div>
<div class="modal-body">
{{ form.as_p }}
<div class="modal-footer">
<button type="submit" class="btn btn-primary col-4">Adjuntar archivo</button>
<button type="button" class="btn btn-secondary col-4" data-dismiss="modal">Cancelar</button>
</div>
</div>
</form>
my_lead_page.html
This is the page where I create the modal form:
{% extends "leads/base.html" %}
{% load static %}
{% block contenido %}
<!-- Lots and lots of info -->
<button type="button" class="btn btn-sm btn-primary col-2" id="btn_agregar_adjunto">
Agregar archivo adjunto
</button>
{% endblock %}
{% block other_scripts %}
<script type="text/javascript" src="{% static 'js/leads/archivos_adjuntos.js'%}"></script>
{% endblock %}
archivos_adjuntos.js
$(function() {
$("#btn_agregar_adjunto").click(function() {
$.ajax({
url: 'adjuntar_archivo/',
type: 'get',
dataType: 'json',
beforeSend: function() {
$("#modal-form").modal("show");
},
success: function(data) {
$("#modal-form .modal-content").html(data.html_form);
}
});
});
$("#modal-form").on("submit", "#js_adjuntar_archivo_form", function() {
var form = $(this);
$.ajax({
url: form.attr("action"),
data: form.serialize(),
type: form.attr("method"),
dataType: 'json',
cache: false,
contentType: false,
processData: false,
success: function(data) {
if(data.form_is_valid) {
alert("Archivo adjuntado");
} else {
$("#modal-form .modal-content").html(data.html_form);
}
}
});
return false;
});
});
Instead of form.serialize() try sending it with js formData() it should work.
Here is an example:
$("#modal-form").on("submit", "#js_adjuntar_archivo_form", function() {
$that = this;
var form = new FormData($(this)[0]);
$.ajax({
url:$that.attr("action"),
type:$that.attr("method"),
data:form,
processData: false,
contentType: false,
// rest of the code'''
});
return false;
});

how to clear the form fields after submit data through ajax in symfony2?

my new.html.twig code is given below. How do I clear the form fields after the form submission?
{% extends '::base.html.twig' %}
{% block body -%}
<h1>Proposals creation</h1>
<form action="{{ path('proposals_create') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<p>
<button type="submit">Create</button>
</p>
</form>
<ul class="record_actions">
<li>
<a href="{{ path('proposals') }}">
Back to the list
</a>
</li>
</ul>
<div id="result"></div>
{% endblock %}
{% block javascripts %}
<script src="{{ asset('js/jquery-1.10.2.js') }}" type="text/javascript"></script>
<script type="text/javascript">
$().ready(function() {
$("form").submit(function(e) {
e.preventDefault();
var $url = $(this).attr('action');
var $data = $(this).serialize();
$.ajax({
type: "POST",
url: $url,
data: $data
}).done(function( result ) {
if(result.success) {
$('#result').html('<span>Monetary expense correctly saved!<br/> The data are:<br/>id: '+ result.id +'<br/>descrition: '+ result.description +'<br/>proposalname: '+ result.proposalname +'<br/>status: '+ result.status +'</span>');
}
});
});
});
</script>
{% endblock %}
Use HTMLFormElement.reset() to revert all <form> fields to their defaults.
$("form").submit(function(e) {
e.preventDefault();
var $url = $(this).attr('action');
var $data = $(this).serialize();
$.ajax({
type: "POST",
url: $url,
data: $data
}).done(function( result ) {
if(result.success) {
$('#result').html('<span> ... </span>');
}
});
this.reset(); // formElement.reset()
});
reset() resets the form to its initial state. This method does the same thing as clicking the form's reset button. If a form control (such as a reset button) has a name or id of reset it will mask the form's reset method.

Django - after submitting invalid form submit button upon click redirects

I have a django project in which i have a form in a div being loaded by ajax.
when I submit the form with empty fields it returns the form with the required fields shown.
when I hit submit again it redirects to the action of the form instead of reloading the form in the div and showing errors as it does the first time I submit.
does anyone have any idea where the error might be occurring? I imagine somewhere in the ajax or view in my django project.
here is what is returned by the second submit:
{"success": false, "form": "<head>\n\n</head>\n<body>\n<form action=\"/cookbook/createrecipe/\" method=\"POST\" name=\"recipeform\" id=\"createrecipeform\">\n\t<table>\n\t\t<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='c5ea952ee2144b377b375d91b0843c75' /></div>\n\t\t<tr><th><label for=\"id_name\">Name:</label></th><td><ul class=\"errorlist\"><li>This field is required.</li></ul><input id=\"id_name\" type=\"text\" name=\"name\" maxlength=\"200\" /></td></tr>\n<tr><th><label for=\"id_author\">Author:</label></th><td><ul class=\"errorlist\"><li>This field is required.</li></ul><input id=\"id_author\" type=\"text\" name=\"author\" maxlength=\"100\" /></td></tr>\n<tr><th><label for=\"id_picture\">Picture:</label></th><td><input type=\"file\" name=\"picture\" id=\"id_picture\" /></td></tr>\n<tr><th><label for=\"id_ingredients\">Ingredients:</label></th><td><ul class=\"errorlist\"><li>This field cannot be null.</li></ul><textarea id=\"id_ingredients\" rows=\"10\" cols=\"40\" name=\"ingredients\"></textarea></td></tr>\n<tr><th><label for=\"id_steps\">Steps:</label></th><td><ul class=\"errorlist\"><li>This field cannot be null.</li></ul><textarea id=\"id_steps\" rows=\"10\" cols=\"40\" name=\"steps\"></textarea></td></tr>\n<tr><th><label for=\"id_prep_time\">Prep time:</label></th><td><ul class=\"errorlist\"><li>This field is required.</li></ul><input type=\"text\" name=\"prep_time\" id=\"id_prep_time\" /></td></tr>\n<tr><th><label for=\"id_type\">Type:</label></th><td><ul class=\"errorlist\"><li>This field is required.</li></ul><select name=\"type\" id=\"id_type\">\n<option value=\"\" selected=\"selected\">---------</option>\n<option value=\"SW\">Sandwich</option>\n<option value=\"AP\">Appetizers</option>\n<option value=\"SD\">Sauces and Dressings</option>\n<option value=\"SS\">Soups and Salads</option>\n<option value=\"VG\">Vegetables</option>\n<option value=\"RG\">Rice, Grains and Beans</option>\n<option value=\"PA\">Pasta</option>\n<option value=\"BR\">Breakfast</option>\n<option value=\"MT\">Meat</option>\n<option value=\"SF\">Seafood</option>\n<option value=\"BP\">Bread and Pizza</option>\n<option value=\"DT\">Desserts</option>\n</select><input type=\"hidden\" name=\"reset_recipe\" id=\"id_reset_recipe\" /></td></tr>\n\t</table>\n\t<p><input type=\"submit\" value=\"Submit\"></p>\n</form>\n</body>"}
here is my ajax code:
<script type="text/javascript">
$(document).ready(function(){
var form = $('form#createrecipeform');
form.submit(function(e) {
e.preventDefault();
console.log('ajax form submission function called successfully.');
//form = $(this);
console.log(form)
var serialized_form = form.serialize();
$.ajax({ type: "POST", 
url: $(this).attr('action'),
data: serialized_form, 
success: (function(data) { 
console.log('ajax success function called successfully.');
data = $.parseJSON(data);
if (data.success) {
console.log('success');
} else {
console.log('failure');
var newForm = data.form;
form.replaceWith(newForm);
}
})
});
return false;
});
});
</script>
here is the view: (createrecipe is the action of the form and account is the page that is loading the ajax)
def createrecipe(request):
print "entering createrecipeview"
if request.method == 'POST':
print "form is a post"
form = RecipeForm(request.POST)
print form.errors
if form.is_valid():
print "form is valid"
form = RecipeForm(initial = {'original_cookbook' : request.user.cookbooks.all()[0]})
form = form.save()
t = loader.get_template('cookbook/create_form.html')
c = RequestContext(request, {
'form': form,
})
data = {
'replace': True,
'form': t.render(c),
'success': True,
}
json = simplejson.dumps(data)
return HttpResponse(json, mimetype='text/plain')
else:
print "form is invalid"
form = RecipeForm(request.POST)
t = loader.get_template('cookbook/create_form.html')
c = RequestContext(request, {
'form':form,
})
data ={
'form': t.render(c),
'success': False,
}
json = simplejson.dumps(data)
return HttpResponse(json, mimetype='text/plain')
def account(request):
user = request.user
if request.user.is_authenticated():
cookbooks = user.cookbooks
if cookbooks.all().exists():
cookbook = cookbooks.all()[0]
form = RecipeForm(initial = {'original_cookbook' : request.user.cookbooks.all()[0]})
recipe_list = cookbook.recipes.all()
else:
raise Http404
else:
return HttpResponseRedirect('/accounts/login')
t = loader.get_template('cookbook/account.html')
c = RequestContext(request, {
'form': form,
'recipe_list': recipe_list
})
return HttpResponse(t.render(c))
here is the create_form.html template:
<head>
</head>
<body>
<form action="{% url cookbook.views.createrecipe %}" method="POST" name="recipeform" id="createrecipeform">
<table>
{% csrf_token %}
{{ form.as_table }}
</table>
<p><input type="submit" value="Submit"></p>
</form>
</body>
and here is the account template that includes the create_form template:
{% extends "cookbook/base.html" %}
{% load pagination_tags %}
{% load comments %}
<h1>{{ user }}'s Cookbook</h1>
<ul>
{% block nav-cookbooks %}
<li><a class="nav-inactive" href="/cookbooks/">Cookbooks</a></li>
{% endblock %}
{% block nav-account %}
<li><a class="nav-active" href="/account/">My Cookbook</a></li>
{% endblock %}
</ul>
{% block content %}
{% autopaginate recipe_list 6 %}
<div id="recipe_cont">
{% for recipe in recipe_list %}
<div class="recipe">
<div class="button">
<img src="{{ STATIC_URL }}chicknbraw.jpg" alt="" height="70" width="70" style="display:inline;" />
<h4>{{ recipe.name }}</h4>
</div>
<h5>{{ recipe.author }}</h5>
<h5>Prep Time: {{ recipe.prep_time }} minutes</h5>
<h6>Add Recipe
Remove Recipe</h6>
</div>
{% endfor %}
</div>
<div id="popupContact" class="popup">
<a id="popupContactClose" style="cursor:pointer;float:right;">x</a>
<p id="contactArea">
<h1 style="text-align:center">Create New Recipe</h1>
{% include 'cookbook/create_form.html' %}
</p>
</div>
<div id="backgroundPopup">
</div>
<div id="col2-footer">
{% paginate %}
<p id="recipe_order_text"> order by: abc|date
</div>
{% endblock %}
{% block footer %}
<a class="create" style="cursor:pointer" >Create New Recipe</a>
{% endblock %}
sorry for putting so much code but it all seems to rely upon another piece of code so I figured all relevant code would be helpful
thanks for any help you can give me
katie
In your javascript, you're hijacking the form so that it submits via ajax, but then you're calling replaceWith on the form, so your hijacked form gets obliterated and is replaced with a new, non-hijacked form. To solve this you can either
1) Only replace the content of the form - this should work since you're only attaching events to the form itself and not its child elements
2) Write your js as a function which you can call firstly on the initial form, and subsequently on any new forms loaded via ajax.
UPDATE: for example,
<script type="text/javascript">
$(document).ready(function(){
function hijack() {
var form = $('form#createrecipeform');
form.submit(function(e) {
e.preventDefault();
console.log('ajax form submission function called successfully.');
//form = $(this);
console.log(form)
var serialized_form = form.serialize();
$.ajax({ type: "POST",
url: $(this).attr('action'),
data: serialized_form,
success: (function(data) {
console.log('ajax success function called successfully.');
data = $.parseJSON(data);
if (data.success) {
console.log('success');
} else {
console.log('failure');
var newForm = data.form;
form.replaceWith(newForm);
hijack();
}
})
});
return false;
});
};
hijack();
});
</script>

Resources