Star Rating Bootstrap Data Input to Flask - python

After much help and many attempts at getting this functional I have now done so. The code below should be sufficient should anyone else also want to do the same.
Jinja2 Template
<div class="panel-footer">
<label for="input-2" class="control-label">My Rating:</label>
<input id="stars_{{result.id}}" name="input-2" class="rating rating-loading" data-min="0" data-max="10" data-step="0.1" data-stars="10" data-size="xs"onchange="updateStars('{{result.id}}')" >
</div>
<form action="{{url_for('delete_f', id=result.id)}}" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="Delete" class="btn btn-danger">
</form>
</div>
JavaScript
{% for result in results %}
<script type="text/javascript">
function updateStars(id) {
var rating = document.getElementById("stars_" + id).value;
$.ajax({
url : '/rate_movie',
headers: {"Content-Type": "application/json"},
type : 'POST',
dataType: 'json',
data : JSON.stringify{'id': 'id', 'rating': rating}
});
};
</script>
{% endfor %}
Flask Backend
#app.route('/rate_movie',methods=['GET','POST'])
def rate_movie():
# Create cursor
if request.method == 'POST':
data = request.get_json(force=True)
rating = data['rating']
id = data['id']
cursor = cnx.cursor()
# Execute
#cursor.execute("UPDATE favourites SET rating=5 WHERE id =49") ## Works
cursor.execute("UPDATE favourites SET rating=%s WHERE id =%s",(rating,id))
#("INSERT INTO favourites(rating)VALUES(%s) WHERE id =%s" ,(rating,id))
# Commit to DB
cnx.commit()
#Close connection
cursor.close()
flash('Movie Rated', 'success')
return redirect(url_for('my_f'))

There are a number of problems here, but your first big issue is that you're using non-unique ids. Make your Ids unique and you can properly reference the value of your hidden input, fix your jQuery listener to look like $('#actually_uniqe_id').on('change', function (){...} instead of the mess it is currently, you can get your value correctly with var rating = document.getElementById("#actually_unique_id").value; and modify your posted data to convey both the id and rating like data: {'id': {{result.id}}, 'rating': rating}.
Never use non-unique IDs. Because of confusion surrounding this point, your current jQuery event listener is meaningless, and your rate_id variable is undefined because document.getElementById("{{result.id}}") fetches the first reference of that id, which is a div that has no attribute 'value'.
Edit: Also, since you have a loop generating potentially lots of rating inputs, drop the event listener for something like:
<input id="value_{{result.id}}" type="hidden" onchange="updateStars('{{result.id}}')" />
and
<script type="text/javascript">
function updateStars(id) {
var rating = document.getElementById("value_" + id).value;
$.ajax({
url : '/dashboard',
type : 'POST',
data : {'id': '{{result.id}}', 'rating': rating}
});
</script>

Related

Read from two different forms in Flask

In my page I have two different forms. I want to read the information from the first form whenever I press a button in the second form. Is this possible?
First form:
<form id="loadData" method="post" action="/loadData">
{% if day %}
Day: <input id="day" name="day" size="5px" value={{day}}>
Month: <input id="month" name="month" size="5px" value={{month}}>
Year: <input id="year" name="year" size="5px" value={{year}}>
{% else %}
Day: <input id="day" name="day" size="5px">
Month: <input id="month" name="month" size="5px">
Year: <input id="year" name="year" size="5px">
{% endif %}
.
.
.
</form>
Second form:
<form id="createFile" method="post" action="/createFile">
<button type="submit">Create</button>
</form>
By clicking the button in the second form I want to read the information in the first one to create a file containing all those information.
I tried something like
#app.route("/createFile", methods=["GET", "POST"])
def createFile():
if request.method == "POST":
day = request.form["day"]
month = request.form["month"]
year = request.form["year"]
return redirect('/')
but I can't manage to read those variable properly.
Despite corresponding in the comments i'm not entirely sure this is your end goal, but let's give it a go?
basically all i did was copy stuff from the links attached in the comment.
a.html:
<form id="form_id" action="/loadData" method="POST">
<input type="text" name="q" value="abcd">
<button type="submit">loadData</button>
</form>
<button id="createFile"> createFile </button>
<script>
function post(path, params, method = 'post') {
// The rest of this code assumes you are not using a library.
// It can be made less verbose if you use one.
const form = document.createElement('form');
form.method = method;
form.action = path;
for (const key in params) {
if (params.hasOwnProperty(key)) {
const hiddenField = document.createElement('input');
hiddenField.type = 'hidden';
hiddenField.name = key;
hiddenField.value = params[key];
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
var form_1 = document.querySelector('#form_id')
document.querySelector('#createFile').addEventListener('click', (e) => {
var data = Object.fromEntries(new FormData(form_1).entries());
post("/createFile",data)
});
</script>
app.py:
from crypt import methods
from flask import Flask, request
app = Flask(__name__)
#app.route("/loadData", methods=["POST"])
def loadData():
data = request.get_data()
return f"<h1 style='color:blue'>loadData data: {data}</h1>"
#app.route("/createFile", methods=["POST"])
def createFile():
data = request.get_data()
return f"<h1 style='color:red'>createFile data: {data}</h1>"
if __name__ == "__main__":
app.run(host='0.0.0.0')
page looks like this:
clicking on loadData:
clicking on createFile:
this whole setup is pretty convoluted and unnecessarily complex. what are you trying to achieve?

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 to display mySQL data from server side to HTML from AJAX

I can't find many tutorials on this, mostly could find ones for PHP. I am trying to create a search bar with an autocomplete feature using python Flask, mySQL, and Ajax. I got things working up to capturing the keystroke and selecting from the database. After that, I can't get any of this to display on the client side.
I've tried using a partial page but none of this will display.
server.py file
#app.route("/search", methods=['POST'])
def search():
output = ''
mysql = connectToMySQL("countries_db")
data_received = json.loads(request.data)
data = data_received['query']
var_data = '%' + data + '%'
mysqlQuery = "SELECT name FROM countries WHERE countries.name LIKE '%s' LIMIT 10;" %var_data
result = mysql.query_db(mysqlQuery)
output += '<ul class="list-unstyled">'
if len(result) > 0:
for country in result:
output += '<li>' + country["name"] + '</li>'
else:
output += '<li>Country Not Found</li>'
output += '</ul>'
return render_template("index.html", result=result)
index.html
<li class="search">
<div class="auto">
<img src="{{ url_for('static', filename='search.png') }}">
<input class="search-bar" type="text" id="country" name="country" aria-label="Search through site content"
placeholder="Search for a Country">
<div class="countryList">
{% for country in result %}
<p>{{country.name}}</p>
{% endfor %}
</div>
</div>
</li>
# Ajax
<script type="text/javascript">
$(document).ready(function () {
$('#country').keyup(function () {
var query = $(this).val();
if (query != '') {
$.ajax({
url: "/search",
method: "POST",
data: JSON.stringify({
query: query,
}),
dataType: "JSON",
contentType: 'application/json;charset=UTF-8',
success: function (data) {
data = data.data
$('#countryList').fadeIn();
$('#countryList').html(data)
},
dataType: 'text'
})
}
})
})
ids are refferred using # and class names are referred using . in Jquery. Replace the # before country-list with '.'

django passing arguments from template to bash script

I am trying to have an input field in the template that the user enters a query and that query goes to the views.py
and from there i m taking the query and pass it as argument to the bash script.
This is what i have for now.
views.py
def home(request):
if request.method == 'POST':
try:
query = request.POST['query']
test = subprocess.check_call(['home/.../bash.sh',
query])
return render(request, 'base.html', {'input': test})
except KeyError:
return HttpResponse("Nothing was submitted!")
base.html
<form action="/" method="post">
{% csrf_token %}
<input type="hidden" name="query" value="{{ input }}">
<input type="submit" value="Submit">
</form>
I am stuck right here..i don't know if i shout request.POST or something else much simpler...cause i don't want to use a form.
I figure it out by creating a script in the html template.
<script>
$(".opener").click(function () {
var thead = $("#mytable").find("thead");
thead.find('th').last().remove();
thead = thead.html();
var row = $(this).parents('tr');
row.find('td').last().remove();
row = row.html();
var table = $(document.createElement('table'));
table.append('<thead>' + thead + '</thead>');
table.append('<tbody><tr>' + row + '</tr></tbody>')
$(".modal").html("").append(table);
$(".modal").dialog({width: 'auto', position: 'top'});
});
</script>

Disable clicking on header elements of django-table2

I am getting some data from DB and displaying it to the users using django-table2. My application need user to select any row and based on that I am loading a different page with data relevant to the row selected in the last page. To get this working I am using two hidden fields who's value will be set when I click on some row and that will be passed at the server side for further processing..
The problem I am facing is when i click on the rows it sets the hidden field with correct value but if i click on the header of the table everything gets messed-up. To allow post back on click of the table I have used {{render_table table_name}} in a form tag..
My tables.py
class testTable(tables.Table): // Have used orderable=False for all the rows
release_date = tables.Column(verbose_name="Release date",orderable=False)
product_name = tables.Column(verbose_name="Product name",orderable=False)
...
class Meta:
orderable = False
Views.py
table = productTable(dic)
table.paginate(page=request.GET.get('page', 1), per_page=5)
params['table'] =table
product.html
<div>
<form method="POST" name="form-product-list" action="{% url "product" %}" id="form-product-list" enctype="multipart/form-data">
<input type="hidden" name="prod_ver" value="0" id="prod_ver" />
<input type="hidden" name="release_date" value="0" id="release_date" />
{% render_table table %}
</form>
</div>
Javascript
function addRowHandlers() {
var table = document.getElementById("prod_details");
var rows = table.rows;
for (var i = 0; i < rows.length; i++) {
rows[i].onclick = (function() {
return function() {
$("#prod_ver").val(this.cells[1].innerHTML);
$("#release_date").val(this.cells[0].innerHTML);
};
})(i);
}
}
Currently when I click on any row I will get prod_ver = 1 and release_date = somedate but when I click on the header I am getting prod_ver = prod_ver and release_date =release_date..
Please look into this and ask if you need any clarification.
So it work, as it is supposed to. You should try to loop over table rows only inside "tbody".
in jquery it should look like this:
$("body").on("click","tableSelector >tbody >tr", function(){
$("#prod_ver").val($(this).children().eq(1));
$("#release_date").val($(this).children().eq(0));
});

Categories