How to configure ajax request with jquery in django? - python

So I was doing a django project by following a youtube video. And I got this error:
"Forbidden (403) CSRF verification failed. Request aborted."
everytime when I submit any data in my sign-up.html file.
Then I realize I did not configure ajax correctly. So I search online and saw the django documentation of ajax. I made some changes in my html code. still, it did not solve my problem.
I am still getting the same result. What is my mistake here. Did I made any mistake while configuring ajax?
"assets/js/django-ajax.js"
This is where i save the jquery file.
Its been a while since I am stuck in this problem.
Thank you for your time
My sign-up.html file:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Instapic</title>
<link rel="stylesheet" href="{% static 'assets/bootstrap/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/Login-Form-Clean.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/styles.css' %}">
</head>
<body>
<div class="login-clean">
<form method="post">
{% csrf_token %}
<h2 class="sr-only">Login Form</h2>
<div class="illustration">
<div style="display: none" id="errors" class="well form-error-message"></div>
<img src="{% static 'assets/img/logo.jpg' %}">
</div>
<div class="form-group">
<input class="form-control" id="username" type="text" name="username" required="" placeholder="Username" maxlength="20" minlength="4">
</div>
<div class="form-group">
<input class="form-control" id="email" type="email" name="email" required="" placeholder="Email" maxlength="100" minlength="6">
</div>
<div class="form-group">
<input class="form-control" id="password" type="password" name="password" required="" placeholder="Password" maxlength="20" minlength="6">
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" id="go" type="submit">Create Account</button>
</div>Already got an account? Login here ...</form>
</div>
<script src="{% static 'assets/js/jquery.min.js' %}"></script>
<script src="{% static 'assets/bootstrap/js/bootstrap.min.js' %}"></script>
<script src="{% static 'assets/js/django-ajax.js' %}"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#go').click(function() {
$.post("ajax-sign-up",
{
username: $("#username").val(),
email: $("#email").val(),
password: $("#password").val()
},
function(data, status){
if (JSON.parse(data).Status == 'Success') {
window.location = '/';
} else {
$('#errors').html("<span>" + JSON.parse(data).Message + "</span>")
$('#errors').css('display', 'block')
}
});
return false;
})
})
</script>
</body>
</html>
My ajax configureation:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

Your click handler does not prevent the event from propagating. When your event handler has run the event will propagate and will eventually submit the form as a regular browser POST. You need to prevent this from happening
$('#go').click(function(event) {
event.stopPropagation();

I would suggest, add your csrf_token in every ajax.post for test, to be sure that generated token in cookies is correct.
Example:
$.ajax({
type: 'POST',
url: "",
data: {
username: $("#username").val(),
email: $("#email").val(),
password: $("#password").val(),
'csrfmiddlewaretoken': getCookie('csrftoken')
}
})
In your code. you defined method getCookie in JS, create variable csrf_token and implement this variable in ajaxSetup. Well, token is changing, but your script does not reload it.
If you want to use ajax.setup, put inside there the method getCookie, to be sure that ajax before send retakes correct token.

Related

detail : "CSRF Failed: CSRF token missing." getting this on network when trying to create or delete or put data in angular 13 and DRF

i need to create source, delete it and update as well, butting getting the same error again and again.
//component.html
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<div class="container mt-5">
<div class="form-group">
<input
type="text"
class="form-control"
placeholder="Search..."
[(ngModel)]="filterTerm"
/>
</div>
<ol>
<li *ngFor="let source of sources$ | async | filter: filterTerm">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{source.name}}</h5>
<p>URL:- <a href ='{{source.url}}'>{{source.url}}</a></p>
<a class="btn btn-primary" href='fetch/{{source.id}}' role="button" style="margin-left:5px ">Fetch</a>
<button class="btn btn-primary" (click)="deleteSource(source.id)" style="margin-left:5px">Delete </button>
<button class="btn btn-primary" (click)="editSource(source.id, source)" style="margin-left:5px ">Edit </button>
<br>
</div>
</div>
</li>
</ol>
</div>
//service.ts
export class SourcesService {
API_URL = 'http://127.0.0.1:8000/sourceapi/';
constructor(private http: HttpClient) { }
/** GET sources from the server */
Sources() : Observable<sources[]> {
return this.http.get<sources[]>(this.API_URL,);
}
/** POST: add a new source to the server */
addSource(source : sources[]): Observable<sources[]>{
return this.http.post<sources[]> (this.API_URL, source);
//console.log(user);
}
deleteSource(id: string): Observable<number>{
return this.http.delete<number>(this.API_URL +id);
}
// component.ts
export class SourcesComponent implements OnInit {
filterTerm!: string;
sources$ !: Observable<sources[]>;
// deletedSource !: sources;
constructor(private sourcesService: SourcesService) { }
// prepareDeleteSource(deleteSource: sources){
// this.deletedSource = deleteSource;
// }
ngOnInit(): void {
this.Source();
}
Source(){
this.sources$ = this.sourcesService.Sources()
}
deleteSource(id : string){
console.log(id)
this.sourcesService.deleteSource(id).subscribe();
}
I tried, providing the header and also tried the steps got in the question I asked yesterday but problem did not solved till now..
Steps I tried :- "detail": "CSRF Failed: CCSRF token missing." when sending post data from angular 13 to django connected database

Python - using ajax to post selected item from select list to Flask

I am trying to write a small web application using Flask. But I have a question while creating selection list. For example, there are 2 selection lists in the web. The first one is "keyID" and second one is "fieldName". When user clicked an item in selection list "keyID" then a corresponding selection list "fieldName" appears.
Here's the HTML:
<div class="control">
<label for="keyid">KeyID of parking spots:</label><br>
<select name ="keyidList" id="keyid_list" style="width: 400px">
{% for keyid in keyIDs %}
<option value ="{{keyid}}">{{keyid}}</option>
{% endfor %}
</select><br><br>
</div>
<div class="control">
<label for="fieldname">FieldName of parking spots:</label><br>
<select name ="fieldnameList" id="fieldname_list" style="width: 400px">
{% for fieldname in fieldnames %}
<option value ="{{fieldname}}">{{fieldname}}</option>
{% endfor %}
</select><br><br>
</div>
Javascript code in HTML file
<head>
<meta charset="utf-8">
<script>
$(function(){
var select_keyid = $('#keyid_list');
var selected_keyid = select_keyid.val();
select_keyid.on('change', function(){
$.ajax({
type: 'POST',
data: JSON.stringify({
'selected_keyid': selected_keyid,
}),
url: "{{ url_for('auth.get_fieldname_list') }}",
success: function(data) {
$("#fieldname").empty();
for (var i = 0; i < data.length; i++) {
$("#fieldname").append('<option value="' +fieldname+ '">' + fieldname + '</option>');
}
}
});
});
});
</script>
</head>
And here's the Python Flask code
#auth.route('/get_fieldname_list', methods=['POST'])
def get_fieldname_list():
req = request.json
keyid = req.get('selected_keyid')
fieldname = get_field_name_from_keyid(keyid)
return fieldname
I personally think that, the problem is with ajax code but I don't know why and how to fix it. Any advice will be really appreciated.

XMLHttp request isn't being processed

I am attempting to take what a user highlights in order to process it for a query of a database. While I seem to be able to successfully get what is being highlighted, it seems that passing that information through to the actual database query isn't happening. There aren't any error messages. I hope there is a typo somewhere and it is an easy fix, in any scenario an extra set of eyes from a more experienced person would be greatly appreciated. I am using Python Flask to develop a web app for personal use, here is the code
{% block content %}
<head>
<style>
div.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
float: right;
width: 30%;
}
</style>
</head>
<h2 style="text-align: center;">{{ name }}</h2>
<div class="sticky" id="sticky_display">
<button type="button" onclick ="getSelectedText()" style="background: darkblue; color: white;">Query Database</button>
<form name="testform">
<textarea id="selectedtext">
</textarea>
</form>
</div>
<div class="row">
<div class="col-xs-8 col-sm-8 col-md-8 col-lg-8" style="white-space: pre-line;" id="text_display">
{{ text }}
</div>
</div>
<script>
function getSelectedText() {
var selectedText = '';
if (window.getSelection) {
selectedText = window.getSelection();
}
// document.getSelection
else if (document.getSelection) {
selectedText = document.getSelection();
}
// document.selection
else if (document.selection) {
selectedText = document.selection.createRange().text;
} else return;
function(selectedText) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200) {
document.getElementById('selectedtext').value = this.responseText;
}
xhttp.open("POST", "/query_database', true);
xhttp.send(selectedText);
}
}
</script>
{% endblock %}
By default, POST will send
Content-Type: application/x-www-form-urlencoded
which requires that send a URL-encoded form. It appears that you're sending plain text, which would require adding
xhttp.setRequestHeader('Content-Type', 'text/plain');
before the .send()

Flask/Jquery - trying to request a value but this error appears: raise exceptions.BadRequestKeyError(key)

I'm currently trying out some Jquery in combination with Flask. The problem which I'm facing is that if I want to do a request of a variable it gives me this Error "raise exceptions.BadRequestKeyError(key)".
Does anyone know how to fix this? I would like to enter a number in the input box and after pressing the "led on" button, I would like to print that value out on my terminal.
#app.route("/led/<int:state>", methods=['POST', 'GET'])
def led(state):
if state == 0:
print("es ist 0")
elif state == 1:
intervall = request.form['intervallMinuten']
print(intervall)
else:
return ('Unknown LED state', 400)
return ('', 204)
<div class="col-md-4">
<label>Intervall in <strong>Minuten</strong>:</label>
<input type="number" id="intervall" step="0.1" name='intervallMinuten'> </input>
<h3>LED</h3>
<button class="btn btn-default" type='button' id='led_on'>LED ON</button>
<button class="btn btn-default" type='button' id='led_off'>LED OFF</button>
</div>
<script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.12.0.min.js"><\/script>')</script>
<script>
$(document).ready(function() {
// Setup button click handlers.
$('#led_on').click(function() {
console.log('LED on!');
$.post('/led/1');
});
$('#led_off').click(function() {
console.log('LED off!');
$.post('/led/0');
});
});
</script>
You need to add 'intervallMinuten' in your post form, like this
<script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.12.0.min.js"><\/script>')</script>
<script>
$(document).ready(function() {
// Setup button click handlers.
$('#led_on').click(function() {
console.log('LED on!');
$.post('/led/1',{intervallMinuten: 2});
});
$('#led_off').click(function() {
console.log('LED off!');
$.post('/led/0', {intervallMinuten: 2});
});
});
</script>

How to upload a file and populate parts of the website with its content?

There are related questions here and here but they don't solve my problem.
Let's say I have a file testdata.csv like this:
A,B
1,3
2,4
and I want the user allow to upload the file and then - just for simplicity - display its content on the same page without affecting any other elements on this page. So my desired outcome would look like this:
How would I do this? I found a couple of approaches that use a form to load the file and send its (modified) content back (to e.g. download it) but I struggle to find a solution to pass the JSON response back to modify the page (here: add the table).
This is my entire code:
from flask import Flask, render_template, request, jsonify
import pandas as pd
import numpy as np
import json
# Initialize the Flask application
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/_get_table', methods=["POST", "GET"])
def get_table():
# how to catch the file here and send its content back?
# file = ????
# print(file)
df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
return jsonify(my_table=json.loads(df.to_json(orient="split"))["data"],
columns=[{"title": str(col)} for col in json.loads(df.to_json(orient="split"))["columns"]])
# if I use a form, I can use
# file = request.files['myfile']
# however, how do I then send the JSON response?
if __name__ == '__main__':
app.run(debug=True)
and my index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<h3 class="text-center text-muted">Some great stuff</h3>
</div>
<hr class="mb-4">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div><br><br>
<button class="btn btn-primary" type="button" id="upload_document">Upload and process!</button>
<hr class="mb-4">
<table id="pretty_table" class="table table-striped"></table>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var table = null;
$('#upload_document').bind('click', function() {
$.getJSON('/_get_table', {
// what to pass here?
}, function(data) {
if (table !== null) {
table.destroy();
table = null;
$("#pretty_table").empty();
}
table = $("#pretty_table").DataTable({
data: data.my_table,
columns: data.columns
});
});
return false;
});
$('.custom-file-input').on('change', function() {
let fileName = $(this).val().split('\\').pop();
$(this).next('.custom-file-label').addClass("selected").html(fileName);
});
});
</script>
</body>
</html>
#charlietfl in the comments and this link got me on the right track. One can use FormData and then an .ajax call to achieve the desired outcome. So the important parts of above's link are (the complete files can be found below):
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div>
</form>
and
// Get form
var form = $('#fileUploadForm')[0];
// Create an FormData object
var data = new FormData(form);
and
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/_get_table",
data: data,
processData: false,
The processData: false, is important to prevent jQuery form transforming the data into a query string as explained in the link above.
On the flask site, we can then easily retrieve the file by doing:
file = request.files['myfile']
df = pd.read_csv(file)
which turns the .csv into a pandas dataframe. Clearly, there should be checks etc before doing this.
The entire HTML page index_formdata.html would look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<h3 class="text-center text-muted">Some great stuff</h3>
</div>
<hr class="mb-4">
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div>
</form><br><br>
<button class="btn btn-primary" type="button" id="upload_document">Upload and process!</button>
<hr class="mb-4">
<table id="pretty_table" class="table table-striped"></table>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var table = null;
$("#upload_document").click(function (event) {
//stop submit the form, we will post it manually.
event.preventDefault();
// Get form
var form = $('#fileUploadForm')[0];
// Create an FormData object
var data = new FormData(form);
// disabled the submit button
$("#upload_document").prop("disabled", true);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/_get_table",
data: data,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
success: function (data) {
if (table !== null) {
table.destroy();
table = null;
$("#pretty_table").empty();
}
table = $("#pretty_table").DataTable({
data: data.my_table,
columns: data.columns
});
$("#upload_document").prop("disabled", false);
},
error: function (e) {
alert(e.responseText);
console.log("ERROR : ", e);
$("#upload_document").prop("disabled", false);
}
});
});
$('.custom-file-input').on('change', function() {
let fileName = $(this).val().split('\\').pop();
$(this).next('.custom-file-label').addClass("selected").html(fileName);
});
});
</script>
</body>
</html>
the flask file as follows:
from flask import Flask, render_template, request, jsonify
import pandas as pd
import numpy as np
import json
# Initialize the Flask application
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index_formdata.html')
#app.route('/_get_table', methods=["POST", "GET"])
def get_table():
# catch file from the form; if you have several files, just make several requests (access by name)
file = request.files['myfile']
df = pd.read_csv(file)
return jsonify(my_table=json.loads(df.to_json(orient="split"))["data"],
columns=[{"title": str(col)} for col in json.loads(df.to_json(orient="split"))["columns"]])
if __name__ == '__main__':
app.run(debug=True)
and the testdata.csv file
A,B,C
1,3,123
2,4,456

Categories

Resources