Python Bottle - forms post/get repeating function every refresh - python

I have a page with a list of items which includes their id, name, price and stock level. This information is gotten from a SQLite3 database and entered onto the page in a table. The table also has a column where the user can add or remove stock of the item.
This code shows a section of the table:
% for i in items:
<tr>
<th scope="row">{{i[0]}}</th>
<td>{{i[1]}}</td>
<td>{{i[2]}}</td>
<td>{{i[3]}}</td>
<td><form method="post" action="/worker"><input type="number" name="newStock.{{i[0]}}"><input style="margin-left: 5px" type="submit" Value="Submit"></form></td>
</tr>
% end
And here is the Python bottle code for this page
#route('/worker', method='GET')
def workerPage1():
return template('worker', items=store.printItems(2)) # 1 prints out for shoppers, 2 prints out for workers.
#route('/worker', method='POST')
def workerPage2():
# allows to receive ItemId
for k in request.forms:
if k.startswith('newStock.'):
itemId = k.partition('.')[-1]
numStock = request.forms.get(k)
store.updateStock(itemId,numStock) # this accesses the database, updating the stock level
return template('worker', items=store.printItems(2))
The problem I am getting, is that when I enter the stock to be added, say for example '8' it does it fine. But then when I refresh the page, it adds another 8 onto it. So if it was at 22 and then I click submit to add the stock, it would go to 30 and then when I refreshed the page it would go to 38, etc.
Any idea on how to stop this from happening?
Thanks.

Instead of return template('worker', items=store.printItems(2)) in workerPage2(), I used return redirect('worker'). Works as I wanted.

Related

How to display error message when clicking button - not using forms - HTML, Python

I am building a movie application. I have set up a movie page with brief details about the movie and 2 button: Watched and Saved, which can save into a users watched list or save for later list.
I have set up my backend so that if a user clicks on the watched or saved button more than once, it should not save the movie to the database table watchedmovies or savedmovies. However i am struggling to display an error message.
Backend Code for watched button snippet:
cursor.execute( "SELECT * FROM watchedmovies WHERE username LIKE %s", [username_new] )
watched_post = cursor.fetchone()
print(watched_post)
message = ""
if watched_post:
message = "You have already saved this movie to your watched list!"
return redirect(url_for('movie', id=id))
else:
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('INSERT INTO watchedmovies VALUES (% s, % s, % s)', (watched_username, id, watched_title))
mysql.connection.commit()
message = 'This movie has been saved to your watched list!'
return redirect(url_for('user', id=id, username_new=username_new, current_post=current_post, profile_post=profile_post, watched_post=watched_post, message=message))
Front end - HTML code for the movie page where for the watched button:
<div class="message">{{ message }}</div>
<a class="btn btn-primary btn-sm mt-1 mb-1" href="{{ url_for('watched', id=current_post.id, username_new=profile_post.username)}}">Watched</a>
I dont really know what I am doing wrong. I have tried using a flash message but that didnt work either.
I am just looking to flash a message when a user clicks on the watched button to try and save a movie they have already saved
For python, you could use this to display an error message.
try:
a=input()
except:
raise Exception('Movie Already Saved')
You could change it slightly to fit your code, for instance, by making a variable that keeps track of if the movie is saved or not. Then, if it is, you could just display the error message you want.
Ex:
Aladin_already_saved=False
def movie_saved(x):
if movie_save_button_clicked:
if x==True:
print("This movie is already saved")
else:
print("Movie successfully saved")
movie_saved(Aladin_already_saved)
It isn't the most convenient way to do it, as you would have to make it the first variable like Cats_already_saved, and run this line for every movie you want to add, but I assume you aren't going to have a catalog as large as something like Netflix, so this should work for you if you adjust it slightly.

Returning more than one object in Flask

I am trying to return two items to an html file in Flask and am having trouble figuring out the best way to do it.
The two operative lines Flask is pulling from in a separate file are:
# trader_db = Blotter(1000000, collection)
cash = self.cash + df['Value'].sum() # trader_db.cash
return df.to_html('templates/pnlstmt.html') # trader_db.pnl(collection)
with this code:
#app.route("/pl")
def show_pl():
cash = trader_db.cash
trader_db.pnl(collection)
return render_template('pnlstmt.html'), cash
I get the expected P&L
Symbol Position
AAPL 350.0
AMZN 1000.0
GOOG 350.0
But the cash isn't displayed.
Is there a way I can get show_pl() to display both objects?
I tried to make cash a DataFrame and concatenate but that threw an error.
The general idea is to separate "logic" from "presentation". Using python function you calculate values. And using template system you structure it in a presentable html view.
I wrote simple example that might be helpful for you:
First, we calculate data (I have a stub here) and pass them to the template context as variables:
from flask import app, render_template
import pandas as pd
main = app.Flask("app")
#main.route('/pnl')
def pnl():
df = pd.DataFrame([["AAPL","AMZN","GOOG"], [350.0,1000.0,350.0]], index=["Symbol","Position"]).T
cash = 100000
# passing data to the template
return render_template("pnl.html", data=df, cash=cash)
main.run(port=8889)
Second, we create template which "renders" our data into html. This is the content of pnl.html file:
<table>
{% for ind,row in data.iterrows() %}
<tr><td>{{ind}}</td><td>{{row["Symbol"]}}</td><td>{{row["Position"]}}</td></tr>
{% endfor %}
</table>
<h2>Total cash: {{ cash }}</h2>
This is a complete code to launch and test it.

Python flask application not displaying generated html file for second time

I have a Python flask application which takes input id's and dynamically generates data into a html file. Below is my app.py file.
#app.route('/execute', methods=['GET', 'POST'])
def execute():
if request.method == 'POST':
id = request.form['item_ids']
list = [id]
script_output = subprocess.Popen(["python", "Search_Script.py"] + list)
# script_output = subprocess.call("python Search_Script.py "+id, shell=True)
# render_template('running.html')
script_output.communicate()
#driver = webdriver.Chrome()
#driver.get("home.html")
#driver.execute_script("document.getElementById('Executed').style.display = '';")
return render_template('execute.html')
#app.route('/output')
def output():
return render_template('output.html')
output.html file has below code at the bottom.
<div class="container" style="text-align: center;">
{% include 'itemSearchDetails.html' %}
</div>
itemSearchDetails.html is generated every time dynamically based on the input. I check for different inputs and it is generating perfectly. When I run it with some input(assume 2) values for the first time, it runs perfectly and shows the output correctly. But, when I run for different values(assume 4) for the next time, the file 'itemSearchDetails.html' is generated for those 4 values but the browser only shows output for the first 2 values. No matter how many times I run it, browser shows only output with the first run values.
So, every time only the first inputted values are shown no matter how many times I run. I am not sure if it is browser cache issue since I tried "disabling cache" in chrome. Still it didn't work. Please let me know if there is something I am missing.
Try solution from this answer:
Parameter TEMPLATES_AUTO_RELOAD
Whether to check for modifications of the template source and reload
it automatically. By default the value is None which means that Flask
checks original file only in debug mode.
Original documentation could be found here.
Looks like Jinja is caching the included template.
If you don't need to interpret the HTML as a Jinja template, but instead just include its contents as-is, read the file first and pass the contents into the template:
with open('itemSearchDetails.html', 'r') as infp:
data = infp.read()
return render_template('execute.html', data=data)
...
{{ data|safe }}
(If you do need to interpret the HTML page as Jinja (as include will), you can parse a Jinja Template out of data, then use the include tag with that dynamically compiled template.)

How do you call a python function instead of a script using a form?

I've been reading the book 'Head First Python' where the writer talks about creating dynamic webpages using a module he created called 'yate', an HTML template engine (which I renamed to site_yate in the code below). The example he works through is a hypothetical coach wanting his athletes to be able to check their times online. The design is as follows: first you enter the homepage which has a link to run a script which generates a webpage where you can select the athlete whose times you want to view. Then when you select your athlete and click submit the form calls another script called "site_generate_timing_data.py" where you can views the athlete's top times. So I decided to take it further and add functionality to add a time for the athlete, using this extra line of code in my python script.
print(site_yate.do_form("addtime.py", [athlete_id]))
The HTML this will generate will be this:
<form action="addtime.py" method="POST">
<h1>Want to add a time?</h1>
<input type="Text" name="1" size=40> //"1" is the athlete's id in this example
<input type="Submit" value="Submit">
</form>
As you can see this code calls the script 'addtime.py' which has the following code:
import cgi
import sqlite3
data = cgi.FieldStorage().value[0] #this attribute will be in the form MininFieldStorage(name, value)
id = data.name #this attribute is the input's name i.e. athlete's id
time = data.value #this attribute is the input's value i.e. the time
connection = sqlite3.connect("NUACDB.sqlite") #my DB's name
cursor = connection.cursor()
cursor.execute("""INSERT INTO timing_data (athlete_id, time)
VALUES (?, ?)""",
(id, time)) #just SQL stuff
connection.commit()
connection.close()
Which works fine, however I want to change a few thing about this, since it leaves the user on a blank page. I could generate some HTML code to provide links to the homepage etc. or even JavaScript code to redirect the user automatically, but I want to keep this script HTML-free so that I can also use it elsewhere.
What I want to do instead is make the script execute on the same page. Not only that, but I would also prefer if I could put the addtime.py code as a function in another module called 'athletemodel.py' and call it form there, i.e. athletemodel.addtime() (or I could do from athletemodel import addtime so I can call the function directly). How can I call a python function using HTML code? I'm aware of the onsubmit="" form attribute but apparently that is for JavaScript functions. Another thing I'm unsure about is whether the data submitted in the form will still be accessible through CGI FieldStorage and hence whether my addtime.py code will still work as it is.
This stuff is so confusing! All help is appreciated.
Not sure if you already had it in mind, but I would use ajax (remember to include the jQuery library). Here's a rough example to get you started if this is what you want. It'll keep them on the same page:
JavaScript file:
$('#submitButtonId').click(function (event) {
event.preventDefault();
$('#submitButtonId').hide();
$('#thinking').show(); //some div with a nice ajax loader gif...
$.ajax({
type: 'POST',
data: $('#formId').serialize(),
url: '/URL_path_to_function',
success: function (data) {
$('#loading').hide();
var response = data.result //now do stuff with your response
}
error: function(error){
console.log('Error')}
});
Python view/function:
import jsonify
if request.method == 'POST':
value = request.form['input value'] #flask...
#Do stuff
return jsonify(result='Some response')

Math on Django Templates

Here's another question about Django.
I have this code:
views.py
cursor = connections['cdr'].cursor()
calls = cursor.execute("SELECT * FROM cdr where calldate > '%s'" %(start_date))
result = [SQLRow(cursor, r) for r in cursor.fetchall()]
return render_to_response("cdr_user.html",
{'calls':result }, context_instance=RequestContext(request))
I use a MySQL query like that because the database is not part of a django project.
My cdr table has a field called duration, I need to divide that by 60 and multiply the result by a float number like 0.16.
Is there a way to multiply this values using the template tags? If not, is there a good way to do it in my views?
My template is like this:
{% for call in calls %}
<tr class="{% cycle 'odd' 'even' %}"><h3>
<td valign="middle" align="center"><h3>{{ call.calldate }}</h3></td>
<td valign="middle" align="center"><h3>{{ call.disposition }}</h3></td>
<td valign="middle" align="center"><h3>{{ call.dst }}</h3></td>
<td valign="middle" align="center"><h3>{{ call.billsec }}</h3></td>
<td valign="middle" align="center">{{ (call.billsec/60)*0.16 }}</td></h3>
</tr>
{% endfor %}
The last is where I need to show the value, I know the "(call.billsec/60)*0.16" is impossible to be done there. I wrote it just to represent what I need to show.
You can do it on three different layers:
Database level. SQL is a powerful language capable of mathematics. You could write your equation in the select part of your query. In your case, that should be along the lines SELECT (duration/60*0.16) FROM cdr;. Examples can be found here and on Google. Note that in this case, stress (algorithm complexity) is put on the MySQL server process and not the Python process.
View level. In your example, just before your return, you could loop over every element of your result variable to modify its value. You can follow the example that was given by Lie Ryan for this level.
Template level. This is done by a custom filter. You can write your custom filter as written in the documentation and pipe your template variable through this filter in order to get the desired value.
Something along these lines would represent a custom filter applicable on your template(s):
#register.filter
def customFilter(value):
return value / 60.0 * 0.16
You would then use it this way in your template, after {% load %}-ing the custom filter (read the documentation for more implementation information):
{{ billsec|customFilter }}
If the math operations are not too complex I normally use custom template tags. Add operation is already available as a template tag and I use the below snippet in my project for multiplication, subtraction and division respectively. Put this code inside a .py file inside your app/templatetags location and also add a __init__.py in there.
from django import template
#Django template custom math filters
#Ref : https://code.djangoproject.com/ticket/361
register = template.Library()
def mult(value, arg):
"Multiplies the arg and the value"
return int(value) * int(arg)
def sub(value, arg):
"Subtracts the arg from the value"
return int(value) - int(arg)
def div(value, arg):
"Divides the value by the arg"
return int(value) / int(arg)
register.filter('mult', mult)
register.filter('sub', sub)
register.filter('div', div)
EDIT: the following answer is totally wrong, since I thought OP was using sqlite. MySQL has its own way of wrapping things into dictionary, see far below.
You can subclass sqlite3.Row and write your own "computed field":
class MyRow(sqlite3.Row):
def comp_billsec(self):
return (self['billsec'] / 60) * 0.16
cursor = ...
cursor.row_factory = MyRow
for r in cursor.execute('...'):
print r['billsec'], r.comp_billsec()
note that our comp_billsec is accessed using method call syntax, while sqlite3.Row factory provides access through dictionary syntax. This discrepancy would disappear in django template since inside django template, dictionary access and zero-argument function call have the same syntax, so you can do {{ call.billsec }} and {{ call.comp_billsec }}.
EDIT: In MySQL, you can insert computed values in the view along the lines of:
cursor = connections['cdr'].cursor(cursorclass=MySQLdb.cursors.DictCursor)
calls = cursor.execute("...")
result = [r + dict(comp_billsec=r['billsec'] / 60 * 0.16) for r in cursor.fetchall()]
return render_to_response("cdr_user.html",
{'calls':result }, context_instance=RequestContext(request))
Additionaly, you should use parameterized query (note the comma instead of %):
cursor.execute("SELECT * FROM cdr where calldate > '%s'", (start_date,))
Your previous code is subject to SQL injection security issue since you're interpolating start_date into the SQL query directly. If start_date contains ' OR 1 OR ', for example, your query will be interpolated as SELECT * FROM cdr where calldate > '' OR 1 OR '' which will select all rows in the table; it could be even worse.

Categories