Aim
To connect the Practical 14 admission form to a database: on Submit the Name, Email and Course are inserted into an SQLite table via sqlite3 using a parameterised INSERT query.
CO Mapping: CO1, CO2, CO3
Theory
SQLite is a serverless, zero-configuration relational database: the engine is a library inside your own process — no server to install, no credentials, no port, unlike client-server systems such as MySQL or PostgreSQL — and the whole database is one ordinary file, or, as here, ":memory:", held purely in RAM. Python ships the sqlite3 driver in its standard library.
Usage follows a fixed lifecycle: connect() opens (or creates) the database, cursor() gives the object that executes SQL, execute() runs one statement, commit() makes changes durable, close() releases the database. Commit is the step beginners forget — writes live in an implicit transaction, and an uncommitted INSERT is rolled back at close. Plain CREATE TABLE works here only because a :memory: database is born empty each run; file databases need the idempotent CREATE TABLE IF NOT EXISTS to survive reruns.
The INSERT is parameterised: VALUES (?, ?, ?) plus a tuple of values keeps data apart from the SQL text, so input can never become SQL syntax. Concatenating or f-stringing values in is the classic SQL injection hole — one apostrophe (O'Brien) breaks the query, and hostile input can rewrite it entirely.
The form is Practical 14's, unchanged; the new idea is where the write sits in event-driven code. submit() is the Button's callback: invoked by the event loop, it reads the entries with .get(), packs form_data and calls insert_admission(conn, form_data). The connection opens once at startup and is passed into the GUI, so every submission reuses it.
Dataset
No input dataset applies — the form stores data; exactly one row is inserted per run:
| Field | Widget | GUI demo | Fallback |
|---|---|---|---|
| Name | name_entry | GUI Student | CLI Student |
email_entry | gui@student.com | cli@student.com | |
| Course | course_entry | BCA | BCA |
Procedure
init_db()connects to":memory:"and creates theadmissionstable (id INTEGER PRIMARY KEY AUTOINCREMENTplus threeTEXT NOT NULLcolumns), commits and returnsconn.insert_admission(conn, data)runs the parameterisedINSERTwith the tuple(data["name"], data["email"], data["course"]), thenconn.commit().run_gui_with_db(conn)rebuilds the P14 form —name_entry,email_entry,course_entryplus a Submit button wired tocommand=submit.submit()packs the.get()values intoform_data, callsinsert_admission(), prints "Inserted via GUI:" and closes the window.root.after(800, autofill_and_submit)types "GUI Student" / "gui@student.com" / "BCA" and presses Submit automatically.- On a headless online compiler tkinter raises instead, and the top-level
exceptinserts thesampledict ("CLI Student") as the fallback. - Either way,
conn.execute("SELECT ...").fetchall()(a connection-level shortcut) reads every row, the loop prints each tuple, andconn.close()discards the in-memory database.
Interpretation of Results
On a desktop the "Admission Form + DB" window fills itself after 0.8 s; the console shows Inserted via GUI: {'name': 'GUI Student', 'email': 'gui@student.com', 'course': 'BCA'}, then Rows in admissions table: and the single tuple (1, 'GUI Student', 'gui@student.com', 'BCA'). On a headless compiler the run reports GUI unavailable, inserted fallback data: with the CLI Student dict and a Detail: line, and the final row becomes (1, 'CLI Student', 'cli@student.com', 'BCA'). Either way the table holds exactly one row, and its leading 1 came from no INSERT — AUTOINCREMENT assigned it, which is why the column list names only name, email, course. Because the database is :memory:, conn.close() erases it — rerun and the table starts empty, id 1 again.
Common Mistakes
- Forgetting
conn.commit()— the row seems present (the open connection can SELECT it back) but is rolled back at close and vanishes. - Building SQL with an f-string, e.g.
f"... VALUES ('{name}')"— one apostrophe breaks it and hostile input can inject SQL; use?placeholders. - Using the connection after
conn.close()— a late SELECT raisessqlite3.ProgrammingError; reconnecting to":memory:"gives a new empty database.
🎯 Viva Questions
- Why is SQLite called serverless? The engine is a library inside the Python process — no separate server or network; the database is one file or RAM (
:memory:). - State the sqlite3 usage lifecycle.
connect()→cursor()→execute()→commit()→close(). - Why
VALUES (?, ?, ?)instead of concatenation? Placeholders send values apart from the SQL text, preventing SQL injection and quoting errors. - Where does the printed id 1 come from? The INSERT omits
id;INTEGER PRIMARY KEY AUTOINCREMENTassigns the next key. - How does the GUI trigger the write? The event loop runs the Submit callback
submit(), which passes the.get()values toinsert_admission(). - How would the data persist across runs? Connect to a file like
"admissions.db"and useCREATE TABLE IF NOT EXISTSso reruns do not fail.