0

this error has getting me for a while now. From what I researched, it commonly occurs when inserting a wrong type in a column field or a value in the wrong order. Even though it appears in check50, I was able to neither find, reproduce nor fix this issue so far.

The error:

sending GET request to /signin
sending POST request to /login
sending POST request to /buy
exception raised in application: ValueError: FOREIGN KEY constraint failed 

My "best" attempt to reproduce this Exception did not generate anything useful. I tried to iterate through the alphabet and two-letter combinations while buying different amounts of stocks, but with no results. As you can see, I could buy all combinations I tried besides a few that didn't exist, but the foreign key error was not raised at all: enter image description here

My Database if as follows:

CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username TEXT NOT NULL, hash TEXT NOT NULL, cash NUMERIC NOT NULL DEFAULT 10000.00);

CREATE TABLE sqlite_sequence(name,seq);

CREATE UNIQUE INDEX username ON users (username);

CREATE TABLE operations (operation_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER NOT NULL, symbol TEXT NOT NULL, name TEXT NOT NULL, shares INTEGER NOT NULL, operation_value REAL NOT NULL, operation_type TEXT NOT NULL, total INTEGER NOT NULL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users (id));

CREATE TABLE history (operation_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER NOT NULL, symbol TEXT NOT NULL, name TEXT NOT NULL, shares INTEGER NOT NULL, operation_value REAL NOT NULL, operation_type TEXT NOT NULL, total INTEGER NOT NULL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users (id));

My "Buy" code:

def buy():
    """Buy shares of stock"""

    if request.method == "GET":
        return render_template("buy.html")

    # Get values
    user_id = session.get("user_id")
    symbol = request.form.get("symbol")
    shares = request.form.get("shares")
    stocks = lookup(symbol)

    # Check if values are according to expected
    if not stocks or stocks == "ERROR":
        return apology("Try another symbol!", 400)


    if not shares.isdigit() or int(shares) <= 0:
        return apology("Incorrect value of shares!", 400)

    price = truncate(stocks["price"])

    total = truncate(price * int(shares))
    name = stocks["name"]
    print(symbol, name)
    cash = db.execute("SELECT cash FROM users WHERE id = ?", user_id)[0]["cash"]

    if cash < price:
        return apology("No money bro")

    # If anything is received, the user has bought the stock, so we have to increment
    user_owns_stock = db.execute("SELECT * FROM operations WHERE user_id = ? AND symbol = ?", user_id, symbol)

    # Either update the value of stocks or add them in a new line
    if not user_owns_stock:
        table_insert_all(table_name="operations", user_id=user_id, symbol=symbol, name=name,
                                    shares=shares, price=price, operation="Buy", total=total)
    else:
        operations_update_shares(type="Buy", shares=shares, value=total, user_id=user_id, stock_symbol=symbol)

    table_insert_all(table_name="history", user_id=user_id, symbol=symbol, name=name,
                                    shares=shares, price=price, operation="Buy", total=total)

    # Only Reduce the money if everything else works
    users_update_cash(type="Buy", value=total, user_id=user_id)

    return redirect("/")

Lastly, the Database-specifc functions from another script:

def table_insert_all(table_name, user_id, symbol, name, shares, price, operation, total):
    """Insert all data on either history or operaions table"""

    db.execute(
        f"""INSERT INTO {table_name} (user_id, symbol, name, shares, operation_value,
        operation_type, total) VALUES(?, ?, ?, ?, ?, ?, ?)""",
        user_id,
        symbol,
        name,
        shares,
        price,
        operation,
        total
    )

def operations_update_shares(type, shares, value, user_id, stock_symbol):
    """Updates the amount of shares a user has"""

    if type =="Sell":
        db.execute(
            """UPDATE operations SET shares = shares - ?, total = total - ?
            WHERE user_id = ? AND symbol = ?""", shares, value, user_id, stock_symbol
        )

    else:
        db.execute(
            """UPDATE operations SET shares = shares + ?, total = total + ?
            WHERE user_id = ? AND symbol = ?""", shares, value, user_id, stock_symbol
        )

def users_update_cash(type, value, user_id):
    """Update cash in user's account"""

    if type == "Sell":
        db.execute(
            """UPDATE users SET cash = cash + ?
            WHERE id = ?""", truncate(value), user_id
        )

    if type == "Buy": #Explicit check
        db.execute(
            """UPDATE users SET cash = cash - ?
            WHERE id = ?""", truncate(value), user_id
        )

I do believe I'm missing something very simple here, but I would really like to understand "why" its wrong instead of "what" is wrong. Thanks in advance!

1 Answer 1

2

A foreign key constraint fail usually means that the code is trying to insert something into table b with data that requires a field that must already exist in table a. In this case, I'd say that the code is trying to insert a row into one of your tables, operations or history, with a userid that doesn't appear in the user table.

You might want to check the values in the vars before executing the insert statements as well as verifying the username is in the user table. BTW, is there some point where the code should verify that the user is in the database, or should add the user to the db?

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

1
  • Thanks for the quick answer! I will make sure to check those out and return to you ASAP. The code does verify if the user is in the database but only during login, I will add a check right before my inserts to confirm. Commented Jul 25, 2022 at 14:04

You must log in to answer this question.

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