I'm having an argument with check50 on pset7 finance, when it comes to the SELL part.

The web app works fine and I'm able to sell my stocks. When I run check50, though, it keeps telling me that the sell page doesn't have all the reuired elements (without specifying which one):

enter image description here

I double-checked my code and I double-checked the requirements and they match. The requirements are asking for a select element named "symbol" and an input text element named "shares". I have both as you can see here:

<form action="/sell" method="post">
    <select class="form-control" id="select-sell" name="symbol">
        <option disabled selected value>Selling</option>
        {% for stock in stocks %}
            <option>{{ stock.get('stock')|upper }}</option>
        {% endfor %}
    <div class="form-group">
        <input autocomplete="off" class="form-control" name="shares" placeholder="Number of shares to sell" type="text"/>
    <button class="btn btn-primary" type="submit">Sell</button>

While this is what's going on in application.py:

def sell():
"""Sell shares of stock"""

# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":

    # Ensure number shares was submitted
    if (not request.form.get("shares")
    or not request.form.get("shares").isdigit()
    or float(request.form.get("shares")) < 0):
        return apology("must specify a non negative number of shares to sell", 400)

    # Get cumulative number of shares for selected stock
    shares_sum = db.execute("SELECT SUM(shares) FROM :sid WHERE stock = :selected_stock",

    # Check if user has enough shares of the stock he/she is trying to sell
    if float(shares_sum[0].get('SUM(shares)')) < float(request.form.get("shares")):
        return apology("cheeky...you cannot sell stocks that you don't have", 400)
    # if he does and he's not selling all his/her shares
    elif float(shares_sum[0].get('SUM(shares)')) >= float(request.form.get("shares")):
        # Update the cash in the "users" table as well
        update_cash = db.execute("UPDATE users SET cash = cash + :sell WHERE id = :sid",
                      sell = float(request.form.get("shares")) * lookup(request.form.get("symbol"))["price"],

        # While recording transaction (sale) to dedicated table in database
        db.execute("INSERT INTO :user (stock, unitprice, shares, spent, cash, date, time)"
                            "VALUES (:symbol, :price, :number, :tot, :cash, :datestamp, :timestamp)",
                            cash=float(db.execute("SELECT cash FROM users WHERE id = :sid", sid=session["user_id"])[0].get("cash")),

    # Redirect user to home page
    return redirect("/")

# User reached route via GET (as by clicking a link or via redirect)
    stocks = db.execute("SELECT DISTINCT stock FROM :sid ORDER BY stock", sid=str(session["user_id"]))
    return render_template("sell.html", stocks = stocks)

I really have no idea what's wrong here (especially 'cause it's working!)

Any help is highly appreciated! Thanks!


The issue here is that, when the index.html page is rendered, the lookup function is returning None for the stock (in this particular case, it's because you are converting your symbols to lowercase before storing them, so check50 is expecting "AAAA" but you've stored "aaaa").

| improve this answer | |
  • Yay all green!! thanks a lot!! I remember when I've put the .lower() part I was thinking that was dodgy and just a stupid temporary solution. I guess I can count this as another great teaching from CS50: code your stuff properly, always! – Andrea Tassera Aug 20 '18 at 0:31

The exception was raised after POSTing to login, before opening the sell page. Meaning your problem is likely in login or index.

Some thoughts on your code:

Do you have a table per user?

    shares_sum = db.execute("SELECT SUM(shares) FROM :sid WHERE stock = :selected_stock",

makes me wonder. Maybe you should have a different approach, storing all the transactions with a field user_id in a common transaction table.

When I use aggregate functions like SUM(shares), I usually write it like SELECT SUM(shares) AS 'sum_shares', defining an alias that will be used for the field name. Experience has shown the automatically chosen name sometimes differs from what you'd expect, and specifying one is more reliable.

| improve this answer | |
  • Is it possible that the error is in sending the POST request to /login when this has passed for all the checks before this one? (edited the image in my initial post to give more context) Is it checking the login / index methods in application.py or the html pages? Also, yes every time someone register the app creates a dedicated table for that user, where the name is the user id (unique) and then every buy/sell it inserts new entries. Considering that theoretically this could be a real-world web app with potentially millions of entries, this way should have been neater/quicker/safer...no? – Andrea Tassera Aug 16 '18 at 22:48
  • About the one table per user, to me it just has a weird feeling (I don't want my requests to be that dynamic), and others also don't seem to like them: stackoverflow.com/questions/7544544/… – Blauelf Aug 17 '18 at 5:16
  • And the error message appears before any sending GET request to /sell (which is the next step), so it has to be in login or index. – Blauelf Aug 17 '18 at 5:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .