AJAX in Django rendering JSON - javascript

I am trying to pass a JSON object to a view. This view will print out the JSON object in a template. This is just a test to see what data I am passing in Django.
Before the JSON object gets sent, I have javascript that will validate the data being sent. If the validation passes then the data is sent via AJAX.
I currently cannot get anything to be sent to the desired view. I am getting a 403 for the POST.
[22/Dec/2014 21:36:52] "POST /test/ HTTP/1.1" 403 2295
Here is the gist of my code: https://gist.github.com/liondancer/a9df593daeeecce7f180
JS:
$(document).ready(function() {
// Data to describe what kind of test
var testData = {
"timestamp": "",
"hive": 0,
"hdfs": 0,
// Contains a list of testData objects
"beacons":[]
};
var testRun = document.getElementById("test-form");
testRun.addEventListener('submit', function(event) {
event.preventDefault();
var selectedTest = document.querySelector('input[name=test-select]:checked');
alert(selectedTest);
var testType = selectedTest.id;
if (testType == "hdfs-test") {
testData["hdfs"] = 1;
testData["hive"] = 0;
} else if (testType == "hive-test") {
testData["hdfs"] = 0;
testData["hive"] = 1;
} else if (testType == "hdfs-hive-test") {
testData["hdfs"] = 1;
testData["hive"] = 1;
} else {
// null
}
var events = document.getElementById("event-textarea").value;
// check in valid input
var eventSource = events.replace("],[","],,,,[");
// beaconLists allows users to submit --> [{beacon1}, {beacon2}, ...], [{beacon3}, {beacon4}, ...]
var beaconLists = eventSource.split(",,,,");
for (var i = 0; i < beaconLists.length; i++) {
// inspect one list in beaconLists [{beacon1}, {beacon2}, ...]
var beaconList = beaconLists[i];
try {
// list of JSON objects
var beaconObjList = JSON.parse(beaconList);
for (var j = 0; j < beaconObjList.length; j++) {
var beaconObj = beaconObjList[j];
if (beaconObj["data"] && beaconObj["application"]) {
// successful parse to find events
// describe beacon being tested
alert("yes");
var beacon = {
"app_name": beaconObj["application"]["app_name"],
"device": beaconObj["application"]["device"],
"device_id": beaconObj["application"]["device_id"],
"os": beaconObj["application"]["os"],
"os_version": beaconObj["application"]["os_version"],
"browser": beaconObj["application"]["browser"],
"beacon": beaconObj
};
// append to testData
testData["beacons"].push(beacon);
// reset beacon so we can append new beacon later
beacon = {};
} else {
// notify event isn't in the correct format?
alert("no");
}
}
} catch (e) {
// notify bad JSON
alert("failed");
}
}
console.log(testData);
//$.ajaxSetup({
// beforeSend: function(xhr, settings) {
// if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
// xhr.setRequestHeader("X-CSRFToken", csrftoken);
// }
// }
//});
$.ajax({
type: "POST",
url: "/test/",
data: testData,
success: function () {
alert("yay");
},
failure: function () {
alert("boo");
}
});
});
});
html:
{% extends 'index/index.html' %}
{% load staticfiles %}
{% block head %}
<script type="text/javascript" src="{{ STATIC_URL }}home/js/home.js" async></script>
<link href="{{ STATIC_URL }}home/css/home.css" rel="stylesheet">
{% endblock head %}
{% block content %}
<div>Welcome to Trinity E2E testing</div>
<form id="test-form">
{% csrf_token %}
<input id="hdfs-test" type="radio" name="test-select" class="btn btn-default btn-lg">HDFS
<input id="hive-test" type="radio" name="test-select" class="btn btn-default btn-lg">HIVE
<input id="hdfs-hive-test" type="radio" name="test-select" class="btn btn-default btn-lg">BOTH
<textarea id="event-textarea" rows="8" class="form-control" placeholder="Events..."></textarea>
<input id="submit-test" type="submit" class="btn btn-default btn-lg" value="Submit">
</form>
{% endblock content %}
home/views.py:
from django.shortcuts import render
def load_homepage(request):
return render(request, 'home/home_page.html', '')
def scan_events(request):
if request == "POST":
# json = request.POST['testData']
# condition statement for file upload ot c/p events
return render(request, 'home/test.html', {'data': request.POST})
test.html:
{{ data }}
urls.py:
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'parser/', include("parser.urls", namespace="parser")),
url(r'^$', 'home.views.load_homepage', name='home'),
# url(r'form_data', 'parser.views.form_handler', name='')
url(r'test/$', 'home.views.scan_events'),
)

try to send CSRF token
$.ajax({
type: "POST",
url: "/test/",
data: {
csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value, testData,
},
success: function () {
alert("yay");
},
failure: function () {
alert("boo");
}
});

Related

Cache displays an unchecked checkbox as checked

I am building a simple todo list where you can create, check and delete todos.
All actions are implemented with AJAX logic so the page doesn't have to be reloaded all the time.
I get an error where an item that is checked=False is displayed as checked=True even though the data that is used to display the checkbox correctly says checked=False.
When the URL is called again instead of doing a refresh everything is displayed correctly which is why I suspect it to be a caching issue.
This process produces the error (all todos are unchecked):
Refresh page
Check first todo
Refresh page (First todo still correctly checked and data says checked=True).
Delete first todo.
Refresh page with ctr or cmd + R
-> Now the first item is correctly deleted but the former second todo is now checked even though it should be unchecked.
The data used to populate the template is also correct with checked=False.
This error does not happen when calling the URL again instead of cmd + R.
This is my index.html:
<html lang="en">
<head>
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Todo App</title>
<style>
.hidden {
display: none;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
</style>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
</head>
<body>
<div class="container text-center">
<h2>Todo App</h2>
<div id="error" class="hidden">Something went wrong!</div>
<form id="form">
<input type="text" id="description" name="description" />
<input type="submit" value="Create" />
</form>
</div>
<ul id="todos" class="container">
{% for d in data %}
<li class="row" id="li_{{d.id}}">
{% print(d) %}
<div class="col text-end">
<input class="check-completed" type="checkbox" id={{d.id}}
{% if d.completed %}
checked
{% endif %}
/>
</div>
<label class="col text-center" for={{d.description}}>{{d.description}}</label>
<div class="col">
<button id="{{d.id}}" type="button" class="btn-close text-start" aria-label="Delete Todo item">
</button>
</div>
</li>
{% endfor %}
</ul>
<script type="text/javascript" src="{{ url_for('static', filename='todoScripts.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
</body>
</html>
This is the todoScripts.js file:
// New todo submit Script
document.getElementById('form').onsubmit = function(e) {
e.preventDefault();
fetch('/todo/create', {
method: 'POST',
body: JSON.stringify({
'description': document.getElementById('description').value,
}),
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(jsonResponse => {
console.log('response', jsonResponse);
data = jsonResponse
const liItem = createLiItem(data["id"], data["description"])
document.getElementById('todos').appendChild(liItem);
document.getElementById('error').className = 'hidden';
//reset form
const form = document.getElementById('form');
form.reset();
})
.catch(function() {
document.getElementById('error').className = '';
})
}
// Checkbox Script
const checkboxes = document.querySelectorAll('.check-completed');
for (let i = 0; i < checkboxes.length; i++) {
const checkbox = checkboxes[i];
checkbox.onchange = function(e) {
console.log('id:', e.target.id, 'is checked', e.target.checked);
fetch('/todo/set-completed', {
method: 'POST',
body: JSON.stringify({
'id': e.target.id,
'completed': e.target.checked
}),
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
console.log('response:', response);
document.getElementById('error').className = 'hidden';
})
.catch(function() {
document.getElementById('error').className = '';
})
}
}
// Delete script
const exes = document.querySelectorAll('.btn-close');
for (let i = 0; i < exes.length; i++) {
const ex = exes[i];
ex.onclick = function(e) {
close_id = e.explicitOriginalTarget.attributes.id.nodeValue
console.log('id:', close_id, 'shall be deleted');
fetch('/todo/delete', {
method: 'DELETE',
body: JSON.stringify({
'id': close_id,
}),
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
console.log('response:', response);
document.getElementById('li_'+close_id).remove(); //remove it from the DOM
document.getElementById('error').className = 'hidden';
})
.catch(function() {
document.getElementById('error').className = '';
})
}
}
And the app.py to run the server:
from flask import Flask, render_template, redirect, url_for, request, jsonify, abort
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import sys
import pprint
app = Flask(__name__) # __name__ names the flask app after the file name (this case 'app')
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://jj#localhost:5432/todoapp"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
completed = db.Column(db.Boolean(), nullable=False, default=False, server_default='true')
def __repr__(self) -> str:
return f"<Todo {self.id}, Done: {self.completed}, description: '{self.description[:20]}'"
#app.route('/')
def index():
todos_from_db = Todo.query.order_by('id').all()
pprint.pprint(todos_from_db)
return render_template('index.html', data=todos_from_db)
#app.route('/todo/create', methods=['POST'])
def create_todo():
error = False
try:
data = request.get_json()
description = data['description'] #get the description from the request body
new_todo = Todo(description=description)
db.session.add(new_todo)
db.session.commit()
except:
db.session.rollback()
error = True
print(sys.exc_info)
finally:
db.session.close()
if error:
abort(400)
else:
return jsonify({'description':description}
)
#app.route('/todo/set-completed', methods=['POST'])
def complete_todo():
error = False
try:
data = request.get_json()
todo = Todo.query.get(int(data['id']))
completed = data['completed']
todo.completed = completed
db.session.commit()
except:
db.session.rollback()
error = True
print(sys.exc_info)
finally:
db.session.close()
if error:
abort(400)
else:
return f"Updated todo with id {data['id']} to completed={data['completed']}"
#app.route("/todo/delete", methods=["DELETE"])
def delete_todo():
error = False
try:
id = int(request.get_json()["id"])
todo = Todo.query.get(id)
db.session.delete(todo)
db.session.commit()
except:
db.session.rollback()
error=True
print(sys.exc_info)
finally:
db.session.close()
if error:
abort(400)
else:
return f"Deleted {todo} with id {id}"
I tried forbidding to cache the page with the meta tag but atleast for firefox that didn't work.
Could someone point out my error or help me forbid caching? Thanks!

How to call an async function from template?

When the user clicks a specific button, I want to call an synchronous function inside the already used view function, but passing a parameter from JavaScript. How can I do it?
Template:
<input class="form-check-input" type="checkbox" value="{{ subject.id }}" id="flexCheckDefault{{ subject.name }}" onclick="checkRequisite(this.defaultValue)">
Javascript:
function checkRequisite(id){
}
View:
if request.user.is_authenticated and request.user.groups.filter(name='student'):
subjects = subject.objects.all()
async def checkResquisite(id):
requisite = Requisite.objects.filter(subject_requisite_id=id)
context = {'subjects': subjects, 'requisite': requisite}
template = loader.get_template('student/subject/select.html')
return HttpResponse(template.render(context, request))
elif request.user.is_authenticated and request.user.groups.filter(name='teacher'):
return render(request, 'admin/home/index.html', {})
else:
return redirect('login')
I think there are a few misconcepts here. Async functions are called from the frontend to the backend (with ajax, fetch...), not the other way around:
async function checkRequisite(id){
response = await fetch(...);
}
Also, normally you would have two different views, I believe just as a good practice to have your code more organized and descriptive of what your views do exactly.
def load_template(request):
...
return render(...)
def ajax_view(request):
...
return JsonResponse(...)
But, to answer your question, the code below does the following:
On the template, with every click on checkboxes search which of them are selected take their value (subject.id), push into a list and send that list of IDs to backend using a post request with the fetch API.
There (on the backend), check the type the request method and filter requisite based on that list of IDs.
student/subject/select.html
{% extends 'base.html' %}
{% block content %}
{% for subject in subjects %}
<label>{{ subject.name }}</label>
<input class="form-check-input" type="checkbox" value="{{ subject.id }}" id="checkbox" onclick="checkRequisite()">
<br>
{% endfor %}
<hr>
<div id="demo"></div>
{% endblock %}
{% block script %}
<script>
async function checkRequisite() {
var id_list = [];
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].type == "checkbox") {
if (inputs[i].checked) {
id_list.push(inputs[i].getAttribute('value'))
}
}
}
var payload = {
subject_ids: id_list,
};
var data = new FormData();
data.append( 'data' , JSON.stringify( payload ) );
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
await fetch("{% url 'core:load-template' %}", {
method: 'post',
body: data
}).then((response) => {
return response.json();
}).then((data) => {
let element = document.getElementById("demo").innerHTML = '';
for (let key in data['requisites']){
let element = document.getElementById("demo").innerHTML += `<p>Requisite: ${data['requisites'][key]['name']} | Subject: ${data['requisites'][key]['subject']}<p><br>`;
}
});
}
</script>
{% endblock %}
views.py
def load_template(request):
if request.user.is_authenticated and request.user.groups.filter(name='student'):
queryset = Subject.objects.all()
requisite = None
if request.method == 'POST':
data = json.loads(request.POST.get('data'))
requisites = Requisite.objects.filter(subject__id__in=data['subject_ids'])
response = {}
for requisite in requisites:
response[requisite.id] = { 'name': requisite.name, 'subject': requisite.subject.name }
return JsonResponse({ 'requisites': response })
return render(request, 'student/subject/select.html', {'subjects': queryset })
elif request.user.is_authenticated and request.user.groups.filter(name='teacher'):
return render(request, 'admin/home/index.html', {})
else:
return redirect('login')
urls.py
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
path('load/template/', views.load_template, name='load-template'),
]

How to do Live search Using Fetch Ajax Method In Django Python?

I am getting Post.match is not a function error when i do this :( Please help, i am a newbie in JavaScript part. (I am getting back an object so have to turn it into an array but still get this error after using the Objects.values Method)
My Views.py File:
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from .models import Post
def Data(request):
items = Post.objects.all()
data = []
for qs in items:
I = {"title":qs.title,
"content": qs.content,
"image": qs.image.url,
}
data.append(I)
return JsonResponse({"data":data})
My HTML File:
{% extends 'blog/base.html' %}
{% load static %}
{% block content %}
<div class = 'w-100 text-center'>
<h1>Search Results</h1>
<form id = "search-form" autocomplete="off">
{% csrf_token %}
<input name = 'game' type="text" id = "search-input" placeholder= "Post Search..">
</form>
<div id = "results-box" class = "results-card">
</div>
</div>
{% endblock content %}
{% block js %}
<script defer src="{% static 'blog/S1.js' %}"> </script>
{% endblock js %}
My Java Script File:
console.log('Heelowwww')
const url = window.location.href
const searchForm = document.getElementById("search-form")
const searchInput = document.getElementById("search-input")
const resultsBox = document.getElementById("results-box")
const csrf = document.getElementsByName("csrfmiddlewaretoken")[0].value
options = {method: "GET",
headers: {
Accept: "application/json"
},
data:{
'csrfmiddlewaretoken': csrf,
}
}
const SearchPosts = async SearchIt => {
const res = await fetch("http://localhost:8000/data/",options)
const Posts = await res.json()
S = Object.values(Posts["data"])
let matches = S.filter(post =>{
const regex = new RegExp(`^${SearchIt}`, 'gi')
return post.match(regex)
})
console.log(matches)
}
searchInput.addEventListener('input', () => SearchPosts(searchInput.value))
My data Json Page:
Json Data Page
Here post.match(regex) your trying to call the match method on a js object. It seems you should be calling it on on of the string properties of it. Something like: post.title.match(regex).

I am trying to create a like and dislike button for different posts on a page using JavaScript and Django but it seems to work first post only

For the most part, I've managed to create a thumbs up and thumbs down icon working as a like and dislike button, it increases and decreases the count as well for the FIRST POST ONLY, and whenever I click on a like button in any post beside the first post, the button doesn't toggle for that post, it toggles for the first post instead and shows count there. When I refresh the page, the count for other posts gets updated but the button never toggles.
main.js
function likeOnClick(id) {
// $('#likes').click(function () {
var postId;
// postId = $('#likes').attr("data-postId");
postId = id;
console.log("I went inside the function", postId);
var currentClass = $('#likes').attr('class');
if (currentClass == 'fa fa-thumbs-up')
{
$("#likes").removeClass("fa fa-thumbs-up");
$("#likes").addClass("fa fa-thumbs-down");
$.get('like_post/', { post_id: postId, ld: 'l' }, function (data) {
$('#like_count').html(data);
// $('#likes').hide();
});
}
else{
$("#likes").removeClass("fa fa-thumbs-down");
$("#likes").addClass("fa fa-thumbs-up");
$.get('like_post/', { post_id: postId, ld: 'd' }, function (data) {
$('#like_count').html(data);
});
}
// });
};
HTML
{% for d in page_obj %}
<div class="secondSection">
<div class="container">
<div class="card" id = "cardID">
<h4>{{d.created_by}}</h4>
{% if request.user == d.created_by %}
Edit
{% endif %}
<p id="contents">{{d.postContent}}</p>
<small>{{d.dateAndTime}}</small>
<strong id = "like_count">{{ d.likes }}</strong>
{% if user.is_authenticated %}
<i class="fa fa-thumbs-up" id="likes" onclick="likeOnClick('{{d.id}}');"></i>
{% endif %}
</div>
</div>
{% endfor %}
function likeOnClick(ele, id) {
var postId;
postId = id;
console.log("I went inside the function", postId);
var currentClass = $(ele).attr('class');
if (currentClass == 'fa fa-thumbs-up')
{
$(ele).removeClass("fa fa-thumbs-up");
$(ele).addClass("fa fa-thumbs-down");
$.get('like_post/', { post_id: postId, ld: 'l' }, function (data) {
$('#like_count').html(data);
// $('#likes').hide();
});
}
else{
$(ele).removeClass("fa fa-thumbs-down");
$(ele).addClass("fa fa-thumbs-up");
$.get('like_post/', { post_id: postId, ld: 'd' }, function (data) {
$('#like_count').html(data);
});
}
};
Please try this and you need to pass this to onclick event handler.

How to delete an object in django without rendering an html page to get the confirmation

I have created a Blog website and I want to add the functionality to delete a post object while clicking on the Delete button without rendering a separate HTML page rather using the confirm method in JavaScript.
And I have no idea to complete that.
i think you use a .remove method in model object or field
model.field.remove(model_object)
and use this for javascript confirm method
<button onclick="myFunction()">DELETE</button>
<p id="demo"></p>
<script>
function myFunction() {
var txt;
var r = confirm("Press a button!");
if (r == true) {
txt = "You pressed OK!";
} else {
txt = "You pressed Cancel!";
}
document.getElementById("demo").innerHTML = txt;
}
</script>
use this javascript code inside template where the delete button present
i Think this will help you
If you want to delete an object without reload the webpage, you will need to use ajax.
You can define a delete url for each post same as you generate an absolute url. It is easy to do. Here is my approach using jquery ajax and function based django view.
#models.py
class MyModel(models.Model):
def delete_url(self):
return reverse('delete-model', kwargs={'pk': self.id}
#views
def delete_model(request, pk):
item = get_object_or_404(Model, id=pk)
if item:
ref = item.id
item.delete()
data = {'ref': ref, 'message': 'Object with id %s has been deleted' %ref}
return JsonResponse(data)
#urls.py
path('delete/<pk>/', delete_model, name='delete-model')
$(document).ready(function () {
$(".comfirm-delete").click(function (e) {
e.preventDefault();
var proceed = confirm('Proceed with deletion');
if (proceed == true) {
var endpoint = $(this).attr('data-url')
$.ajax({
method: 'GET',
url: endpoint,
success: function (data) {
$.notify({
// options
title: '<b>Message<b> ',
message: data.message,
}, {
// settings
type: 'success',
delay: 3000,
allow_dismiss: true,
});
$("#item" + data.ref).hide(1000)
},
error: function (error_data) {
console.log(error_data);
$.notify({
title: '<b>Error</b><br>',
message: 'Sorry, something gone wrong'
}, {
type: 'danger',
delay: 3000,
})
}
})
} else {
e.preventDefault();
$.notify({
title: '<b>Action cancelled</b><br>',
message: 'This one is not <span class="text-danger">Deleted</span>'
}, {
type: 'info',
delay: 3000,
})
}
})
})
.comfirm-delete {
color: red;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-notify/0.2.0/css/bootstrap-notify.css" integrity="sha256-ibUTW+jDj+F8d1T1KZ4DOujRmVTFfvMKL9y14QgEaPQ=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-notify/0.2.0/js/bootstrap-notify.min.js"></script>
<table>
<tbody>
{% for obj in objects %}
<tr id="item{{obj.id}}">
<td>{{obj.info}}</td>
<td data-url="{{obj.delete_url}}" class="comfirm-delete">Delete</td>
</tr>
{% endfor %}
</tbody>
</table>

Categories