Dug into the docs and trawled all the usual online sources but starting to suspect that I'm trying to solve this in a way that's impossible!
This is how I've edited the index page to allow user input to buy/sell shares without leaving the page:
Here's the HTML in my index.html template that renders the top table that I'm trying to make interactive:
<tbody>
{% for row in portfolio %}
<tr>
<td>{{ row["name"] }}</td>
<td>{{ row["SUM(quantity)"] }}</td>
<td>{{ row["price"] }}</td>
<td>{{ row["value"] }}</td>
<form id={{ row["symbol"] }} action="/" method="post">
<td><input class="form-control" name="shares" placeholder="Quantity to trade" type="text"></td>
<td><button class="btn btn-primary" type="submit" name="transaction" value="buy">Buy</button></td>
<td><button class="btn btn-primary" type="submit" name="transaction" value="sell">Sell</button></td>
</form>
</tr>
{% endfor %}
</tbody>
</table>
Here's the Python from the relevant route of my Flask app:
@app.route("/", methods=["GET", "POST"])
@login_required
def index():
"""Show portfolio of stocks"""
# Select user cash from users table in database and assign returned list of dict to variable
cash = db.execute("SELECT cash FROM users WHERE id = :id",
id=session["user_id"])
# Select all records relating to this user from transactions table in database and assign returned list of dict to variable
portfolio = db.execute("SELECT symbol, SUM(quantity) FROM transactions WHERE user_id = :user_id GROUP BY symbol",
user_id=session["user_id"])
# Initialise rolling total for value of all stocks in portfolio to zero
stocks_total = 0
# For each stock in portfolio (list of dicts)
for i in range(len(portfolio)):
# Fetch current price for this stock
current = lookup(portfolio[i]["symbol"])
# Append current price and calcuated value to the dict for this stock in portfolio
portfolio[i].update(name = current["name"], price = current["price"], value = (current["price"] * int(portfolio[i]["SUM(quantity)"])))
# Add value for this stock to rolling total for value of all stocks
stocks_total += portfolio[i]["value"]
# Format price and value using dollars function
portfolio[i]["price"] = usd(portfolio[i]["price"])
portfolio[i]["value"] = usd(portfolio[i]["value"])
# Calculate total value of all assets
assets_total = cash[0]["cash"] + stocks_total
# User reached route via POST (by submitting a form via POST)
if request.method == "POST":
# Ensure valid quantity was submitted
if int(request.form.get("shares")) <= 0:
return apology("must provide quantity", 403)
# FOR DEBUGGING ONLY
print(request.form("id"))
print(lookup(request.form("id")))
# Query API based on and assign result to variable
price = lookup(request.form("id"))
# Display error if stock can't be found
if price == None:
return apology("stock can't be found - see https://www.investopedia.com/terms/s/stocksymbol.asp", 403)
# Calculate total value of transaction
total = price["price"] * int(request.form.get("shares"))
# if buy button clicked
if request.form["transaction"] == "buy":
# Calculate total value of transaction
total = price["price"] * int(request.form.get("shares"))
# Query database to check user's balance
balance = db.execute("SELECT cash FROM users WHERE id = :id",
id=session["user_id"])
# Display apology if user has insufficient funds for requested purchase
if float(balance[0]['cash']) < total:
return apology("insufficient funds available", 403)
# If user has sufficient funds, complete transaction by updating transactions and users tables in database
inserted = db.execute("INSERT INTO transactions (user_id, symbol, price, quantity) VALUES(:user_id, :symbol, :price, :quantity)",
user_id=session["user_id"],
symbol=request.form.get("symbol"),
price=price["price"],
quantity=(request.form.get("shares")))
updated = db.execute("UPDATE users SET cash = :new_balance WHERE id = :id",
new_balance=(float(balance[0]['cash']) - total),
id=session["user_id"])
# Display result of lookup to user
return render_template("bought.html", shares=request.form.get("shares"), name=price["name"], total=usd(total))
# if sell
elif request.form["transaction"] == "sell":
# Query database to check that stocks to be sold exist
available = db.execute("SELECT SUM(quantity) FROM transactions WHERE user_id = :user_id AND symbol = :symbol",
user_id=session["user_id"], symbol=request.form.get("symbol"))
# Display apology if user has insufficient shares for requested sale
if float(available[0]['SUM(quantity)']) < int(request.form.get("shares")):
return apology("insufficient shares available", 403)
# Select user cash from users table in database and assign returned list of dict to variable
cash = db.execute("SELECT cash FROM users WHERE id = :id",
id=session["user_id"])
# Calculate new balance
new_balance = cash[0]['cash'] + total
# If user has sufficient shares, complete transaction by updating transactions and users tables in database
inserted = db.execute("INSERT INTO transactions (user_id, symbol, price, quantity) VALUES(:user_id, :symbol, :price, :quantity)",
user_id=session["user_id"],
symbol=request.form.get("symbol"),
price=price["price"],
quantity=(-(float((request.form.get("shares"))))))
updated = db.execute("UPDATE users SET cash = :new_balance WHERE id = :id",
new_balance=new_balance,
id=session["user_id"])
# Display result of lookup to user
return render_template("sold.html", shares=request.form.get("shares"), price=price, total=total)
# User reached route via GET (by clicking a link or via redirect)
else:
# Pass user data from transactions data to render template
return render_template("index.html", portfolio=portfolio, cash=usd(cash[0]["cash"]), assets=usd(assets_total))
I'm trying to pull through the stock symbol from the row of the table that the user would interact with to initiate a transaction. The stock symbol isn't displayed in the table, but I've tried embedding it as an id in various HTML elements of the table in my index.html Jinja template. Currently it's embedded in the <form>
tag as id={{ row["symbol"] }}
. I've tested that this produces ids that accurately reflect the relevant stock symbols, but I can't figure out whether it's possible to pull these through into the POST
section of my route so that the symbol can be used to call the API to complete the transaction.
Tracebacks helped pinpoint the problem, so I've tried printing return value for debugging:
# FOR DEBUGGING ONLY
print(request.form("id"))
print(lookup(request.form("id")))
# Query API based on and assign result to variable
price = lookup(request.form("id"))
Here's the most recent traceback for reference:
These are the articles I've read that seem to come closest to describing the issue I'm facing:
https://stackoverflow.com/questions/19794695/flask-python-buttons
https://stackoverflow.com/questions/32022568/get-value-of-a-form-input-by-id-python-flask
https://stackoverflow.com/questions/10434599/get-the-data-received-in-a-flask-request
https://stackoverflow.com/questions/77552/id-is-a-bad-variable-name-in-python
Any help hugely appreciated.
Thanks!