How do I get my ajax POST (preventDefault) to work? - javascript

I am trying to upload a csv file to a Flask server and I do not want the page to be reloaded, so I am trying to implement ajax. However, I cannot get it to work. Here is a minimal working example.
app.py
import os.path
from flask import Flask, render_template, request
app = Flask(__name__, static_url_path='', static_folder='static', template_folder='templates')
app.config['UPLOAD_FOLDER'] = './upload/'
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
f = request.files['todo']
f.save(os.path.join(app.config['UPLOAD_FOLDER'], 'upload.csv'))
return 'this should not be printed'
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
templates/index.html
<!DOCTYPE html>
<html>
<body>
<form method="post" id="todo-form" enctype="multipart/form-data">
<input type="file" name="todo" id="todo">
<button type="submit">submit</button>
</form>
<script src="https://code.jquery.com/jquery-3.5.1.js"
integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
crossorigin="anonymous"></script>
<script src="test.js"></script>
</body>
</html>
static/ajax.js
$(document).on('submit', '#todo-form', function(e) {
const fd = new FormData(this);
e.preventDefault();
$.ajax({
type: 'POST',
url: '/',
data: fd,
contentType: false,
processData: false,
complete: function() { alert('saved');},
})
});
The reloading action is now prevented, and the alert is now shown, but the file is not uploaded. Can someone tell me what I did wrong? Thank you very much!

As pointed out by Phil, this is caused by the storage of browser cache. The code works as intended once the cache is cleared.

Related

Django & AJAX combined

;0mage to process it on server and get a response without refreshing the page. I look up for some tutorials how to use AJAX and jQuery to do this but I don't have much knowledge at this matter. I'm completly stuck and have no idea what shall I do next. So far my code looks like this:
models.py
class Snip(models.Model):
#some fields
snip = models.FileField(upload_to="snips/")
latex = models.TextField(default = '')
forms.py
from .models import Snip
from django import forms
class SnipForm(forms.ModelForm):
class Meta:
model = Snip
fields = ['snip']
HTML:
<form id="snipForm" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrfmiddlewaretoken" value="...">
<div class="row align-items-center">
<div class="col-12">
<label for="id_snip">Snip:</label>
<input type="file" name="snip" required="" id="id_snip" class="btn-success p-2 rounded">
<input type="submit" name="upload" id="upload" class="btn btn-secondary">
</div>
</div>
</form>
JavaScript/AJAX
var upload_btn = document.getElementById("upload")
upload_btn.addEventListener('click', function () {
var form_data = new FormData();
var ins = document.getElementById('id_snip').files.length; //
if(ins == 0) return console.log("No snip!!!")
form_data.append("file[]", snip.files[0]);
csrf_token = $('input[name="csrfmiddlewaretoken"]').val();
form_data.append("csrfmiddlewaretoken", csrf_token);
headers = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'};
console.log(form_data);
console.log(headers);
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');
}
});
$.ajax({
type: 'POST',
url: 'https://localhost:8000/docs/', // point to server-side URL
dataType: "json",
ContentType: "application/json",
cache: false,
processData: false,
headers: headers,
data: form_data,
success: function (response) { // display success response
console.log("SUCCESSSSSSSSS")
},
error: function (response) {
console.log("NOPEEEEEE")
}
});
});
views.py
from django.shortcuts import render
from django.contrib.auth.models import User
from .models import Snip
from .forms import SnipForm
from django.http import JsonResponse
from django.core import serializers
def generate(request):
is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
if is_ajax and request.method == "POST":
file = request.FILES['snip']
processed_image = "Some text from OCR"
return JsonResponse({'text':'Yay, I got text from OCR'})
if request.method == 'GET':
Users = User.objects.all()
form = SnipForm()
user_list = []
for user in Users:
if user.get_full_name():
user_list.append(user.get_full_name())
return render(request, "new.html", {'Users': user_list, 'form': form})
To sum up:
The image is loaded to id_snip input tag. After clicking on id="upload" button it should be sent to django server and then to outer OCR API to receive some text back. Then it's supposed to be displayed on the front end without refreshing the page. However after clicking submit I get error:
The view docs.views.generate didn't return an HttpResponse object. It returned None instead.
My first thought was to check the is_ajax value. It occured to be false. So my question is:
How can I check if the request "is AJAX"? I know in previous versions of django it was is_ajax() method but since some like 3.1 version is not recommended
Is there any simple way to do it? My goal is to put received response (text) somewhere else on the page.
#UPDATE:
So I changed a bit of JavaScript/AJAX code above due to this post
I added
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest');
}
});
However on Django error page I can't see any META info such as HTTP_X_REQUESTED_WITH.
What more it seems that no data is appended to the form_data:
Output of:
console.log(form_data)
console.log(headers)
is:
FormData {}
{HTTP_X_REQUESTED_WITH: 'XMLHttpRequest'}
For now I get exactly the same error message, which is The view docs.views.generate didn't return an HttpResponse object. It returned None instead.
What might be wrong with this code? It seems fine for me

Flask web API doesn't recognize image files which were sent via AJAX

I have a very basic Flask API which works with a DNN model.
Python backend is like this:
from flask import request
from flask import jsonify
from flask import Flask
import io
app = Flask(__name__)
#app.route("/predict", methods=["POST"])
def predict():
response = {"success": False}
if request.method == "POST":
if request.files.get("image"):
image = request.files["image"].read()
image = Image.open(io.BytesIO(image))
#...
response = {"success": True}
else:
print('Wrong file')
else:
print('Wrong method')
return jsonify(response)
Simplified JavaScript frontend:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input id="image_selector" type="file">
<button id="predict_button">Predict</button>
<p style="font-weight: bold;">Predictions</p>
<p>Prediction: <span id="prediction_text"></span></p>
<img id="selected-image" src="">
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
let base64Image
var fd = new FormData();
$('#image_selector').change(function() {
let reader = new FileReader()
reader.onload = function(e) {
let dataURL = reader.result
$('#selected-image').attr("src", dataURL)
base64Image = dataURL.replace("data:image/jpeg;base64,", "")
console.log(base64Image)
}
fd.append('file', $('#image_selector')[0].files[0])
reader.readAsDataURL($('#image_selector')[0].files[0])
$('#prediction_text').text("")
})
$('#predict_button').click(function(event) {
let message = {
image: base64Image
}
console.log(message)
$.ajax({
url: "http://localhost:5000/predict",
type: 'POST',
data:fd,
contentType: false,
processData: false,
success: function (response) {
if (response.error) {
$('#prediction_text').text('ERROR')
}
else {
$('#prediction_text').text(response.prediction)
}
console.log(response)
},
error: function () {
},
});
})
</script>
</body>
</html>
When I try to post an image file, in the Flask app the image couldn't pass the second if and prints "Wrong file"
Since this cURL command works just fine:
curl -X POST -F image=#001.jpg 'http://127.0.0.1:5000/predict'
I guess there is a problem with the file format but I am not sure what it is.
try request.files["image"] or request.files['file'] instead of request.files.get("image")
for better implementation, check Upload image in Flask
fd.append('file', $('#image_selector')[0].files[0])
Here changing the "file" to "image" solved the issue. I didn't know names actually matter in this case.
fd.append('image', $('#image_selector')[0].files[0])

Text not changing with render_template after adding Ajax in Flask App

I created a simple AJAX to send data to a flask route without refreshing the page, the data seems to get to the route but it isn't changing the text after render_template.
MyForm is a simple dropdown. I'm trying to display the text of the current selection after form submitted or button clicked without refreshing the page by using AJAX.
run.py
from flask import Flask, redirect, render_template, request, jsonify
from forms.myform import MyForm
import json
import plotly
import pandas as pd, numpy as np
server = Flask(__name__)
server.config.update(dict(
SECRET_KEY="powerfulsecretkey",
WTF_CSRF_SECRET_KEY="a csrf secret key"
))
#server.route("/", methods=["POST", "GET"])
def index():
form = MyForm()
choice = request.form.get('options')
if form.validate_on_submit():
choice = request.form.get('options')
print(choice)
return render_template("index.html", form=form, choice=choice)
if __name__ == "__main__":
server.run()
form
from flask_wtf import FlaskForm
import wtforms
class MyForm(FlaskForm):
options = wtforms.SelectField(label="State", choices=[("SF", "SF"),
("LA", "LA")])
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST" id="fm" action="/">
{{ form.csrf_token }}
<div>{{ form.options}}</div>
</form>
<button type="submit" form="fm" value="Submit">Submit</button>
<div>{{ choice }}</div>
</body>
<footer>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$('form').on('submit', function(e){
$.ajax({
type: 'POST',
data:$('form').serialize(),
url: '/',
success: function(data) {
console.log(data);
},
error: function(jqXHR) {
alert("error: " + jqXHR.status);
console.log(jqXHR);
}
});
e.preventDefault();
});
// Inject our CSRF token into our AJAX request.
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", "{{ form.csrf_token._value() }}")
}
}
})
</script>
</footer>
</html>
Browser
Because you are a form submitted by ajax,The page is not re-rendered,Jinja2 is a rendering to take effect

Normal form submitting correctly but ajax form submits null

I have developed a super simple REST API in Flask using Flask-RESTful for development purposes. It takes text from a POST method and returns json data with the submitted text.
The code for the API:
api.py
from flask import Flask, request
from flask_restful import Resource, Api, reqparse
from flask_cors import CORS, cross_origin
app = Flask(__name__)
api = Api(app)
CORS(app)
class APITest(Resource):
parser = reqparse.RequestParser()
parser.add_argument('text')
def get(self):
return {'message': 'welcome to the api!'}
def post(self):
args = self.parser.parse_args()
_text = args['text']
return {'text': _text}
api.add_resource(APITest, '/api')
if __name__ == '__main__':
app.run(debug=True)
Now, the following HTML works completely fine and I get the response I'm looking for with no errors:
index.html
<form method="post" action="http://localhost:5000/api">
<input type="text" name="text" id="text-input">
<input type="submit" value="Send!">
</form>
returns: { 'text': 'whatever text i submitted' }
If I try to do the exact same thing, but with AJAX, I get a different result:
index.html
...
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="submit.js"</script>
...
<form>
<input type="text" name="text" id="text-input">
<input type="submit" value="Send!">
</form>
submit.js
$("form").submit((e) => {
e.preventDefault();
var fd = new FormData()
fd.append('text', $('#text-input').val())
$.ajax({
url: 'http://localhost:5000/api',
type: 'POST',
data: fd,
processData: false
});
return false;
})
returns {'text': null}
Things I've tried
replacing ${'text-input').val() with document.getElementById('text-input').value
various different ways of sending "form data" including var fd = { text: ${'text-input').val() }
One thing you can try is just grabbing the form data using jquery rather than building it yourself. Example:
<form id="respond_form" enctype="multipart/form-data">
<input type="text" name="text" id="text-input">
<input type="file" name="fl" id="fl">
<input type="submit" value="Send!">
</form>
$("#respond_form").submit((e) => {
e.preventDefault();
var form_data = new FormData();
var file_data = $("#fl").prop("files")[0];
form_data.append("file", file_data)
$.ajax({
url: 'http://localhost:5000/api',
type: 'POST',
data: form_data,
processData: false
});
return false;
})

Flask Dynamic data update without reload page

i'm trying to create something like Google Suggest Tool (via suggest api http://suggestqueries.google.com/complete/search?output=toolbar&hl=ru&q=query )
I'm listening input changes, and send data go GET:
$("#search_form_input").keyup(function(){
var some_var = $(this).val();
$.ajax({
url: "",
type: "get", //send it through get method
data:{jsdata: some_var},
success: function(response) {
},
error: function(xhr) {
//Do Something to handle error
}
});
After that i'm handling this data and send it to Google API and got response in Python:
#app.route('/', methods=['GET', 'POST'])
def start_page_data():
query_for_suggest = request.args.get('jsdata')
if query_for_suggest == None:
suggestions_list = ['',]
pass
else:
suggestions_list = []
r = requests.get('http://suggestqueries.google.com/complete/search?output=toolbar&hl=ru&q={}&gl=in'.format(query_for_suggest), 'lxml')
soup = BeautifulSoup(r.content)
suggestions = soup.find_all('suggestion')
for suggestion in suggestions:
suggestions_list.append(suggestion.attrs['data'])
print(suggestions_list)
return render_template('start_page.html', suggestions_list=suggestions_list)
In Jinja trying to print it in HTML dynamically:
<label id="value_lable">
{% for suggestion in suggestions_list %}
{{ suggestion }}
{% endfor %}
</label>
But variable in Jinja doesn't update dynamically and print empty list.
How to print suggestions from list dynamically in HTML?
Working example:
app.py
from flask import Flask, render_template, request
import requests
from bs4 import BeautifulSoup
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/suggestions')
def suggestions():
text = request.args.get('jsdata')
suggestions_list = []
if text:
r = requests.get('http://suggestqueries.google.com/complete/search?output=toolbar&hl=ru&q={}&gl=in'.format(text))
soup = BeautifulSoup(r.content, 'lxml')
suggestions = soup.find_all('suggestion')
for suggestion in suggestions:
suggestions_list.append(suggestion.attrs['data'])
#print(suggestions_list)
return render_template('suggestions.html', suggestions=suggestions_list)
if __name__ == '__main__':
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>Suggestions</title>
</head>
<body>
Search: <input type="text" id="search_form_input"></input>
<div id="place_for_suggestions"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$("#search_form_input").keyup(function(){
var text = $(this).val();
$.ajax({
url: "/suggestions",
type: "get",
data: {jsdata: text},
success: function(response) {
$("#place_for_suggestions").html(response);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
</script>
</body>
</html>
suggestions.html
<label id="value_lable">
{% for suggestion in suggestions %}
{{ suggestion }}<br>
{% endfor %}
</label>

Categories