how to loop variables from Python controller to JavaScript with flask? - python

I want to show nested content on website:
eg:
phrase1: apple
tweet including phrase1: I like apple.
phrase2: banana
tweet including phrase2: banana is best.
my dataset which in python controller is[["apple","including phrase1: I like apple."],["banana","banana is best."]]
my html file is:
{% extends "layout.html" %}
{% block body %}
<p>Click the button to loop from 1 to 6, to make HTML headings.</p>
<button onclick="myFunction2()">Try it</button>
<div id="demo"></div>
<script>
function myFunction2() {
var x ="", i;
for (i=1; i<=2; i++) {
x = x +"<h2 class=\"phrase\">phrase"+i+":"+"{{phrases[i]}}"+"</h2>";
}
document.getElementById("demo").innerHTML = x;
}
</script>
{% endblock %}
but it only shows:
Click the button to loop from 1 to 6, to make HTML headings.
phrase1:
phrase2:
didn't show any phrase. but when I use {{phrases[1]}} {{phrases[2]}},it can show normally. can't I use i loop every variable?

You can use ajax with your flask backend. First, create the HTML template to display the button, and create a second, smaller template to loop over the dataset:
home.html:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<p>Click the button below to access tweet list from 1-6</p>
<button class='tweets'>View Tweets</button>
<div id='results'></div>
</body>
<script>
$(document).ready(function() {
$('.tweets').click(function() {
$.ajax({
url: "/get_tweets",
type: "get",
data: {'tweets': 'yes'},
success: function(response) {
$("#results").html(response.result);
},
error: function(xhr) {
//pass
}
});
});
});
</script>
</html>
display_data.html:
{%for tweet in tweets%}
<div class='tweet' style='border:solid;border-color:black'>
<p>{{tweet.title}}: {{tweet.phrase}}</p>
<p>{{tweet.including}}</p>
</div>
{%endfor%}
Then, in your .py file, create the necessary flask routes:
import flask
import typing
app = flask.Flask(__name__)
class Tweet(typing.NamedTuple):
phrase:str
including:str
title:str
#app.route('/tweets', methods=['GET'])
def tweets():
return flask.render_template('home.html')
#app.route('/get_tweets')
def view_tweets():
datasets = [["apple","including phrase1: I like apple."],["banana","banana is best."]]
new_set = [Tweet(a, b, f'phrase{i}') for i, [a, b] in enumerate(datasets, start=1)]
return flask.jsonify({"result":flask.render_template('display_data.html', tweets = new_set)})

Related

Fetching data to Django view working manually but not with Selenium. Why?

I am using Selenium to test a Django app I have created.
The following code works fine 'manually' but does not work with Selenium.
Why ?
## dummy.html ##
{% extends 'main.html' %}
{% load static %}
{% block title %}
Dummy page
{% endblock title %}
{% block meta %}
<meta name="robots" content="noindex">
{% endblock meta %}
{% block content %}
<div class="container">
<div class="row">
<div class="col d-flex justify-content-center align-items-center">
<h1 class="text-primary font-handwriting">Dummy page</h1>
</div>
</div>
<br>
<div class="row">
<div class="col d-flex justify-content-center align-items-center">
<h2 class="text-primary font-heading">THIS IS A DUMMY PAGE</h2>
</div>
</div>
<br>
<div class="row">
<button id="trigger-dummy-button" type="button"
class="btn btn-primary text-white font-base fw-bold text-uppercase"
title="Trigger dummy button">Trigger dummy button</button>
</div>
</div>
<script>
var someDummyURL = "{% url 'home_page:another_dummy_view' %}";
</script>
<script type="module" src="{% static '/js/dummy.js' %}"> </script>
{% endblock content %}
## dummy.js ##
'use strict';
var newValue;
async function postDummyFunction(request) {
var response = await fetch(request);
if (response.ok) {
var updatedReturnedValue = await response.json();
newValue = updatedReturnedValue.returnedValue;
alert('-- postDummyFunction -- | newValue = ' + newValue);
// When the script is triggered manually by clicking, response.ok = true and newValue = "Hello World OK !"
return newValue;
} else {
// When the script is triggered by Selenium, response.ok = false
alert('-- postDummyFunction -- | response.ok ? ' + response.ok);
return false;
}
};
document.addEventListener("DOMContentLoaded", function () {
var triggerDummyButton = document.getElementById("trigger-dummy-button");
var someDummyVariable = "Hello World ";
var toPostURL = someDummyURL;
triggerDummyButton.onclick = (e) => {
var data = {"dummyValue": someDummyVariable};
var request = new Request(toPostURL, {
method: "POST",
mode: "same-origin",
headers: {
"Content-Type": "application/json",
'X-Requested-With':'XMLHttpRequest',
"Accept": "application/json",
'X-CSRFToken': csrftoken
},
body: JSON.stringify(data),
});
// Works with Selenium
// if alert(data.dummyValue), browser correctly displays "Hello World "
postDummyFunction(request);
}
});
## views.py ##
import json
from django.shortcuts import render, redirect
from django.http import JsonResponse
def dev_dummy_script(request):
context = {}
return render(request, "dummy.html", context)
def another_dummy_view(request):
if request.method == "POST":
post_data = json.loads(request.body.decode("utf-8"))
posted_value = post_data["dummyValue"]
value_to_return = posted_value + " " + "OK !"
response = JsonResponse({"returnedValue": value_to_return})
return response
## test_selenium.py ##
import time
import os
from decimal import Decimal
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from core.settings import BASE_DIR
PATH_TO_WEBDRIVER_CHROME = os.path.join(
BASE_DIR, "test", "webdrivers", "chromedriver.exe"
)
PATH_TO_WEBDRIVER_FIREFOX = os.path.join(BASE_DIR, "test", "webdrivers", "geckodriver")
SHORT_TIME = 2.5
LONG_TIME = 6
DEFAULT_WAIT_TIME = 10
class FooTest(StaticLiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox(executable_path=PATH_TO_WEBDRIVER_FIREFOX)
def tearDown(self):
self.browser.close()
def testfoo(self):
self.browser.get(("%s%s" % (self.live_server_url, "/dev/dummy_script/")))
elem_trigger_btn = WebDriverWait(self.browser, DEFAULT_WAIT_TIME).until(
EC.element_to_be_clickable((By.ID, "trigger-dummy-button"))
)
elem_trigger_btn.click()
time.sleep(LONG_TIME)
self.browser.close()
versions
Django==3.2
selenium==3.141.0
Firefox 96.0.3
If I trigger python manage.py runserver, manually head to dummy_script and click on the button, it works fine, i.e. alert window pops up and displays "Hello World OK !".
If I trigger the script with pytest and selenium, the code is executed, but response returned from fetch is ko.
EDIT
main.html
<!DOCTYPE html>
{% load static %}
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="{% static 'css/main.css' %}" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<script src="{% static 'js/bootstrap.js' %}"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4"
crossorigin="anonymous"></script>
<script type="text/javascript">
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// 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;
}
const csrftoken = getCookie('csrftoken');
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<title>
{% block title %}
{% endblock title %}
</title>
{% block meta %}
{% endblock meta %}
</head>
<body class="mt-sm-5 mt-md-5 mt-lg-5 mt-xl-5">
<section class="page-content py-3" id="page-content">
{% block content %}
{% endblock content %}
</section>
<script type="module" src="{% static '/js/main.js' %}"> </script>
</body>
</html>
I realise that when I use Selenium, the function getCookie return csrftoken = null. As a result, It is impossible to fetch data to django view.
To be able to make it work with Selenium in test mode only, I have added the following conditional decorator before 'another_dummy_view'.
views.py
def conditional_decorator(dec, condition):
def decorator(func):
if not condition:
# Return the function unchanged, not decorated.
return func
return dec(func)
return decorator
def dev_dummy_script(request):
context = {}
return render(request, "dummy_2.html", context)
#conditional_decorator(csrf_exempt, DEBUG)
def another_dummy_view(request):
if request.method == "POST":
post_data = json.loads(request.body.decode("utf-8"))
posted_value = post_data["dummyValue"]
value_to_return = posted_value + " " + "OK !"
response = JsonResponse({"returnedValue": value_to_return})
return response

no values from jquery in a template to my view function

I am using django and I am getting all id from checkboxes and now I want to pass them to my view function, but I don't know how. I've tried request.GET.getlist('vals') but with no success. any suggestions?
events.html:
<script>
$(document).ready(function(){
$("button").click(function(){
type: 'POST';
var vals = [];
$.each($("input[name='checkb']:checked"),function(vals){
vals.push($(this).attr('id'));
});
alert("values: " + vals.join(", "));
});
});
</script>
<td><button><i class="bi bi-sim"></i></button></th>
{% for l in object_list %}
<tr>
<td>
<form>
<label><input type="checkbox" id={{l.pk}} name="checkb"></label>
<form>
...
urls.py:
path('eprint',views.eprint,name='eprint'),
views.py:
def eprint(request):
print('eprint')
v=request.GET.getlist(vals)
ok I finally solved this problem. I am using AJAX to send the id's to my view:
java script in the template:
<script>
$(document).ready(function() {
$("button").click(function() {
var vals = [];
$.each($("input[name='checkb']:checked"), function() {
vals.push($(this).attr('id'));
});
console.log('cons2:',vals)
$.get('eprint/ticked/',{marked: vals})
console.log('after')
});
});
</script>
views.py:
def eprint(request):
print('eprint')
print(request.GET)

How to load content into div element with flask

I'd like to fill / update a div area in index.html with the result from the python function, but I don't know how to do this. I know there are several other questions with a similar topic but I couldn't succeed with them because they were too specific. I'm pulling my hair out over this.
Would be someone so nice and guide me?
This is a function in main.py:
#app.route('/')
def index():
return render_template('index.html')
#app.route('/stat/')
def stat():
a = 2
b = 10
return(str(a) + ' is not ' + str(b))
this is the index.html:
<body>
<form action="/stat/">
<button type="submit" id="btn1" class="btn btn-primary btn-sm">check stat</button>
</form>
<div id="stat_content"></div>
</body>
As #S3DEV points out, you will need to pass the string to the template via an additional argument. For example, we might do something like this:
#app.route('/stat/', methods=['GET', 'POST']) # EDIT
def stat():
a = 2
b = 10
text = f"{a} is not equal to {b}"
return render_template("index.html", text=text)
In the code above, we set text to be the string to be passed to the template. In the template, we will be able to access this string variable as text.
Now when index.html is rendered, it will be looking for the text variable that is passed in from the Flask application. This is taken care of by Jinja 2, which is the rendering engine used by Flask.
<div id="stat_content">
{% if text %}
<h2>No text to show</h2>
{% else %}
<h2>{{ text }}</h2>
{% endif %}
</div>
Using Jinja 2 syntax with curly braces, we first check if the text variable exists or not; if it does not exist, we render the message, "No text to show." This will happen when we first route into "/", or the default home route of the Flask app.
Once the user fills out the form, however, they will be redirected to "/stat/", at which point we will now have generated text and passed it back to index.html via the render_template("index.html", text=text) function call. Then, when Jinja 2 renders index.html, it will see that text was passed over from the Flask app and display that message, namely that 2 is not equal to 10.
You want this initiated from the button right? Here's how to achieve that with ajax...
<body>
<form action="/stat/">
<button type="submit" onclick="GetData();" id="btn1" class="btn btn-primary btn-sm">check stat</button>
</form>
<div id="stat_content"></div>
</body>
<script type="text/javascript">
function GetData() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) { // XMLHttpRequest.DONE == 4
if (xmlhttp.status == 200) {
document.getElementById("stat_content").innerHTML = xmlhttp.responseText;
}
else if (xmlhttp.status == 400) {
alert('There was an error 400');
}
else {
alert('something else other than 200 was returned');
}
}
};
xmlhttp.open("GET", "/stat/", true);
xmlhttp.send();
}
</script>
to update the content of that div, i think (based on your logic) you need to perform an ajax call to your stat function with the two parameters a and b submitted via POST request:
<form class="form-stat needs-validation" novalidate role="form">
<div class="form-group">
<input type="text" class="form-control" name="a" value="">
<div class="invalid-feedback"></div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="b" value="">
<div class="invalid-feedback"></div>
</div>
<button type="submit" id="btn1" class="btn btn-primary btn-sm">check stat</button>
</form>
<div id="stat_content">Output: ?</div>
put the javascript code below after jquery call
<script>
$(document).ready(function() {
"use strict";
$('.form-stat').submit(function(e) {
e.preventDefault();
$.ajax({
url: "{{ url_for('stat') }}",
type: 'POST',
cache: false,
dataType: 'json',
data: $('.form-stat').serialize(),
success: function(data) {
// console.log(data);
$('.form-stat input[name=a]').val(''); // reset field
$('.form-stat input[name=b]').val(''); // reset field
$('#stat_content').html(data); // update div with the returned vlue
}
});
});
});
</script>
and you have to make little change to your stat function so you can submit dynamically the two parameters via POST like so :
from flask import Flask, request, make_response
import json
#app.route('/stat', methods=['POST'])
def stat():
if request.method == 'POST':
a = request.form['a']
b = request.form['b']
# check if inputs are valid to work with ..
res = str(a) + ' is not ' + str(b) if a != b else str(a) + ' and ' + str(b) + ' are equal.'
resp = make_response(json.dumps(res))
resp.status_code = 200
return resp

How do i update comment count using Ajax

i am working on a project in Django where users can comment on a post. How do i update the comment count of each post, when user comment on a post increases the count to 1. I tried adding the id to div's but nothing happened. How do i implement this?
Home Template:
<!-- Comment count post is an object of all post in homepage -->
<div class="col-4 col-md-4 col-lg-4" id="newfeeds-form">
<a href="{% url 'site:comments' post.id %}" class="home-comment-icon z-depth-0">
<img src="{{ '/static/' }}images/comment.png" width="19" height="19" alt="comment-icon">
{% if post.comments.all|length > 999 %}
<span class="font-weight-bold dark-grey-text" id="number-of-comments">
{{ post.comments.count|floatformat }}
</span>
{% else %}
<span class="font-weight-bold dark-grey-text" id="number-of-comments">
{{ post.comments.count }} Comment{{ post.comments.count|pluralize }}
</span>
{% endif %}
</a>
</div>
<!-- New Feeds Comment Form -->
<div id="newfeeds-form">
{% include 'ajax_newfeeds_comments.html' %}
</div>
Ajax submit comment:
$(document).ready(function() {
$('.feeds-form').on('submit', onSubmitFeedsForm);
$('.feeds-form .textinput').on({
'keyup': onKeyUpTextInput,
'change': onKeyUpTextInput // if another jquery code changes the value of the input
});
function onKeyUpTextInput(event) {
var textInput = $(event.target);
textInput.parent().find('.submit').attr('disabled', textInput.val() == '');
}
function onSubmitFeedsForm(event) {
event.preventDefault();
// if you need to use elements more than once try to keep it in variables
var form = $(event.target);
var textInput = form.find('.textinput');
var hiddenField = form.find('input[name="post_comment"]');
$.ajax({
type: 'POST',
url: "{% url 'site:home' %}",
// use the variable of the "form" here
data: form.serialize(),
dataType: 'json',
beforeSend: function() {
// beforeSend will be executed before the request is sent
form.find('.submit').attr('disabled', true);
},
success: function(response) {
// as a hint: since you get a json formatted response you should better us "response.form" instead of response['form']
$('#newfeeds-form' + hiddenField.val()).html(response.form);
// do you really want to reset all textarea on the whole page? $('textarea').val('');
textInput.val(''); // this will trigger the "change" event automatically
},
error: function(rs, e) {
console.log(rs.resopnseText);
},
complete: function() {
// this will be executed after "success" and "error"
// depending on what you want to do, you can use this in the "error" function instead of here
// because the "success" function will trigger the "change" event automatically
textInput.trigger('change');
}
});
}
});
If you are sure that a new comment will be created with each request, than you can do it with incrementing the count on your desired html element.
I have not worked with python or django so far, but have tried to optimize the code.
<!-- ... -->
<div class="col-4 col-md-4 col-lg-4" id="newfeeds-form">
<span class="font-weight-bold dark-grey-text" id="number-of-comments" data-number="{{ post.comments.count }}">
{% if post.comments.count > 999 %}
{{ post.comments.count|div:1000|floatformat:1 }}k Comments
{% else %}
{{ post.comments.count }} Comment{{ post.comments.count|pluralize }}
{% endif %}
</span>
</div>
<!-- ... -->
function onSubmitFeedsForm(event) {
// ...
$.ajax({
// ...
success: function(response) {
$('#newfeeds-form' + hiddenField.val()).html(response.form);
textInput.val('');
// how you can increment the value of the amount of comments
refreshNumberOfComments();
},
// ...
});
// ...
}
// ...
function refreshNumberOfComments() {
var numberOfCommentsElement = $('#number-of-comments');
var numberOfComments = parseInt(numberOfCommentsElement.data('number')) + 1;
numberOfCommentsElement.data('number', numberOfComments);
if (numberOfComments == 1) {
numberOfCommentsElement.text(numberOfComments + ' Comment');
} else if (numberOfComments > 999) {
numberOfCommentsElement.text((numberOfComments / 1000).toFixed(1) + 'k Comments');
} else {
numberOfCommentsElement.text(numberOfComments + ' Comments');
}
}
Another option is to give the request the information about the amount of comments.
So you could make it in jQuery like this example
$.ajax({
// ...
success: function(response) {]
$('#newfeeds-form' + hiddenField.val()).html(response.form);
textInput.val('');
// your server side script should implement a new field "number_of_comments"
refreshNumberOfComments(response.number_of_comments); // for this call the function above has to get a parameter
},
// ...
});

flask jquery ajax call that is executing on every loop

I want to iterate over files in a folder and and render links that execute a file from a flask interface.
The html/js I wrote executes the file selected by the user as many times as there are files in the folder. where do i need to be more specific so it only executes once?
{% for item in restartFiles %}
<script type=text/javascript>
$(function() {
$('a.calculate').bind('click', function() {
var item = $(this).attr('id');
$.getJSON($SCRIPT_ROOT + '/restartajax/'+item, {
}, function(data) {
$("span.result").text(data.result);
});
return false;
});
});
</script>
<h4>{{item}}</h4>
<span class="result">?</span>
<p>restart {{ item }}
</div>
{%endif%} {%endfor%}
The view, just in case
#app.route('/restartajax/<computer>')
def restartajax(computer):
def runJob(computer):
try:
subprocess.call(r"\\covenas\decisionsupport\meinzer\production\bat\restart\%s" % computer)
except Exception,e:
print 'there was an exception', e
thr = Thread(target = runJob, args = [computer])
thr.start()
return jsonify(result="restarting "+computer+" please wait 10 minutes")
You have placed your <script> tag inside a for loop. Move it outside the loop, preferably after the loop.
{% for item in restartFiles %}
<h4>{{item}}</h4>
<span class="result">?</span>
<p>restart {{ item }}
</div>
{%endif%} {%endfor%}
// THE SCRIPT IS NOW HERE
<script type=text/javascript>
$(function() {
$('a.calculate').bind('click', function() {
var item = $(this).attr('id');
$.getJSON($SCRIPT_ROOT + '/restartajax/'+item, {
}, function(data) {
$("span.result").text(data.result);
});
return false;
});
});
</script>

Categories