I'm have a challenge inserting into the database when I buy shares. I've made life a little complicated (or sophisticated!) because I decided to insert a separate step in the buying process: I start with the "/buy" route and "buy()" method and a simple page that lets the user insert the share symbol and number of shares they want to buy, as below:
Then I've created another route called "/buying" and associated method "buying()", which gives the user a summary of the purchase they want to make, and asks them to confirm the purchase. I just thought it was a better user experience, plus the spec asks you to add at least one personal touch!:
My challenge is that my code to insert generates the following error, from within the IDE: "Positional argument follows keyword argument" It looks like the IDE is highlighting the part of my SQL insert code where it says id=session["user_id"].
I'm sure I've made this way too complicated - for one thing, I've chosen to store lots of the variables as globals in order to pass them between the two different routes [and I'm sure there's a better way of doing this - I just don't understand Python well enough yet! Any tips here would also be appreciated!]
Relevant parts of my code are below:
First, the buy route and method:
@app.route("/buy", methods=["GET", "POST"]) @login_required def buy(): """Buy shares of stock.""" # if user reached route via POST (as by submitting a form via POST) if request.method == "POST": # ensure share symbol was submitted if not request.form.get("symbol"): return apology("You must submit a share symbol") # ensure number of shares to buy was submitted elif not request.form.get("num_shares_buy"): return apology ("You must submit a number of shares to buy") # retrieve the number of shares to buy, store as a global so can pass to buying() method global num_shares_buy num_shares_buy = int(request.form.get("num_shares_buy")) # ensure number of shares to buy is a positive integer if num_shares_buy < 1: return apology ("You must submit a positive whole number of shares to buy") # lookup symbol using lookup() method buy = lookup(request.form.get("symbol")) # add "num_shares_buy" variable to the "buy" dict buy["num_shares_buy"] = num_shares_buy # store share price in US dollars in the "buy" dict buy["price_usd"] = usd(buy["price"]) # store price NOT IN US dollars as a global, so can pass to buying() method global price price = float(buy["price"]) # store name as a global, so can pass to buying() method global name name = str(buy["name"]) # calculate cost of shares to buy (number x price), and add to "buy" dict buy["cost"] = num_shares_buy * buy["price"] global cost cost = float(buy["cost"]) # store cost of shares to buy as US dollars in the "buy" dict buy["cost_usd"] = (usd(buy["cost"])) # extract the user's cash balance from finance.db - call this "result" # NB, db.execute returns a *list* of *dict* objects, each of which contains some key-value pairs # we can be confident the list only contains one dict object, since the ids are unique to each user result = db.execute("SELECT cash FROM users WHERE id = :id", id = session["user_id"]) # now access the dict at position  on the list and call this "result1"... result1 = result # ...and then access the value of the "cash" entry in the dict global cash cash = float(result1["cash"]) # ...finally store as US dollars. NB done as a separate step because... # ...applying the usd() function results in a string, so a test for > won't work cash_usd = usd(cash) # store symbol as a global variable in order to pass to SQLite insert statement in buying() method global symbol symbol = str(request.form.get("symbol")) # ensure symbol entered is valid (i.e. returns data when lookup() queries yahoo finance) if buy == None: return apology("You must submit a valid share symbol") # Matt note - seems a bit odd to put this if-statement here, rather than just after defining buy dict object # But it needs to be here, otherwise python won't accept the else-statement that follows # print the details of the desired share purchase else: return render_template("buying.html", placeholder = buy, cash_usd = cash_usd) # sending to buying.html the dict "buy", plus the variable "cash" # else if user reached route via GET (as by clicking a link or via redirect) else: return render_template("buy.html")
Then the buying route and method
@app.route("/buying", methods=["GET", "POST"]) @login_required def buying(): """Complete Stock Purchase""" # if user reached route via POST (as by submitting a form via POST) if request.method == "POST": # ensure sufficient cash available to purchase shares if cost > cash: return apology("You do not have enough cash to make this purchase") # insert details of transaction into the "transactions" table in finance.db else: result = db.execute("INSERT INTO transactions (id, transaction_date, symbol, name, price, number_of_shares, cost) VALUES (:id, :transaction_date, :symbol, :name, :price, :number_of_shares, :cost)", id=session["user_id"], symbol=symbol, name=name, price=price, num_shares_buy, cost=cost) # send the user somewhere. In the spec the page to render is the index... # ...but I haven't done this yet, so I'm going to return an apology saying "Shares Bought" return apology("Shares Bought") # else if user reached route via GET (as by clicking a link or via redirect) else: return render_template("buying.html")
Many thanks in advance for your help!