Text not changing with render_template after adding Ajax in Flask App - javascript

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

Related

How can I post data to FastAPI using JavaScript and AJAX?

I am trying to to post some data via a request body to my server and for some reason I am getting a msg: "value is not a valid dict", type: "type_error.dict" error.
The backend (built in FastAPI) is working fine, as I am able to get a proper response when using FastAPI's Swagger UI.
I am quite new to JavaScript and AJAX (I mainly work with Python), so I think the issue must be coming from the AJAX function I setup.
My code
main.py (Backend)
from typing import Union
from fastapi import FastAPI, Form
from starlette.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost:8080",
"http://127.0.0.1:5500",
"*"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
from pydantic import BaseModel
class Item(BaseModel):
id: int
name: str
description: str
#app.post("/items_via_request_body")
def read_item_via_request_body(item: Item):
#return {"item_id": item.id, "item_description": item.description}
return {"Hello": "World"}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<h1>Hello world</h1>
<button id="request_body_param">Test Request body Parameters</button><br>
<script>
$(document).ready(function(){
// Request body parameters
$("#request_body_param").click(function(){
console.log('request_body_param pushed')
$.ajax({
url: "http://127.0.0.1:8000/items_via_request_body",
type: "POST",
data: {
id: 123,
name: "John",
description: "I am a description"
},
dataType: "json",
success: function(data){
$("#content").html(data);
console.log("SUCCESS: " + JSON.stringify(data));
},
error: function(data){
$("#content").html("Failed to load data. " + JSON.stringify(data));
console.log("ERROR: " + JSON.stringify(data));
},
complete: function(data){
console.log("COMPLETED: " + JSON.stringify(data));
},
});
});
});
</script>
</body>
</html>
Any help is really appreciated!
from fastapi import FastAPI, Request
# code-stack as in question...
#app.post("/items_via_request_body")
def read_item_via_request_body(request: Request):
form_data = request.form()
# ... Data management operations here ...
return form_data

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

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.

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])

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