0%

Chapter 5 · practice

Change Data Safely

Bind a Value to a Familiar Query

The catalog can answer a fixed question, but a real program must search for whichever asset tag its caller provides. For this run, Python calls the new with a visible tag:

find_item(connection, "TL-100")

The are an already-open connection and the text "TL-100". Open catalog_changes.py. The function already contains a complete query whose WHERE has a question mark:

def find_item(connection, asset_tag):
    statement = """
        SELECT id, asset_tag, name, category, loan_days
        FROM items
        WHERE asset_tag = ?
    """
    return connection.execute(statement).fetchone()

The ? is a placeholder for one value. The SQL structure stays fixed while Python supplies the value separately. Change only the return line:

return connection.execute(statement, (asset_tag,)).fetchone()

The call to execute now has two arguments: the statement and a one-item containing the search value.

SQL positionPython argument value
first and only ?, for asset_tagfirst and only element of (asset_tag,)

Follow the value through the call once. Python receives "TL-100" as asset_tag. The statement still contains WHERE asset_tag = ?; Python does not paste the tag into that text. Instead, (asset_tag,) carries "TL-100" beside the statement, and the database matches that first tuple value to the first placeholder. An apostrophe-containing tag follows exactly the same path. A missing tag does too: it simply produces no matching row. This separation lets one statement handle all three cases without changing its shape.

The supplied connection matters as well. run_changes.py has already opened it and prepared the catalog you can see. By executing through that same connection, the function asks a question of the expected catalog rather than quietly creating a different one somewhere else.

The comma in (asset_tag,) matters. Parentheses alone can group one value, but the comma makes this a one-item tuple that the database interface can match to one placeholder.

Press Run and find the two exact calls:

== catalog_changes.py: find_item ==
find_item(connection, "TL-100")
(3, 'TL-100', 'Cordless drill', 'tools', 7)
find_item(connection, "NO-404")
None

The first result follows the selected column order. The second call uses the same statement with a different value and receives None because no row matches.

run_changes.py prepares a fresh catalog for each named function and prints the call and relevant result. Leave that file unchanged; your function stays in catalog_changes.py and uses the connection it receives.

Submit tries ordinary tags, missing tags, apostrophes, and SQL-looking text. Keep the complete value outside the statement and pass it unchanged in the second argument. Your function now has one stable query that can safely search for many different values.

Task

In catalog_changes.py, pass (asset_tag,) as the second to connection.execute(statement, ...). Keep the fixed query and return .fetchone().

Run the project, check both calls, and submit the .