🗄️ DevAscent Academy • Core Python Textbook Series

Unit 15: Database Connectivity & CRUD Operations

The enterprise persistence handbook. Transition from volatile in-memory data to permanent, ACID-compliant relational databases. Master the standard Python Database API (PEP 249 DB-API 2.0), grasp SQL fundamentals (DDL, DML, DQL, TCL), connect with embedded SQLite and enterprise client-server MySQL, execute secure parameterized queries immune to SQL injection, manage atomic transaction commits & rollbacks, and implement full CRUD (Create, Read, Update, Delete) lifecycles.

Curriculum Level 1st Year BCA / B.Tech / Diploma
Unit Code PY-115 (Database Engineering)
Content Balance 50% Theory • 30% Code • 20% Flowcharts
Target Standard University Exam & Enterprise Data Persistence
📐 Formula Verified: 50% Comprehensive Theory 30% Verified Code 20% Visual Flowcharts
1

Introduction to Databases, RDBMS, and the ACID Model

📌 What is a Database and Why Not Plain Files (TXT / CSV)?

A Database is an organized, persistent collection of structured data designed for rapid search, secure multi-user access, and fault-tolerant storage.
While writing data to flat files (like .txt or .csv) works for trivial programs, real-world systems fail with flat files due to:

  • No Concurrency Control: If two users write to a CSV file simultaneously, data is corrupted or lost (Race Conditions).
  • Slow Query Performance: Searching for a student record requires scanning the entire file line by line ($O(N)$ time), whereas indexed databases locate records in $O(\log N)$ time.
  • No Integrity Constraints: Plain files cannot enforce unique primary keys or valid foreign keys.

Relational Database Management Systems (RDBMS): Organize data into tables consisting of Rows (Records/Tuples) and Columns (Attributes/Fields) with relational links governed by Primary Keys and Foreign Keys.

ACID Pillar Formal Meaning Real-World Banking Example
A — Atomicity "All or Nothing". All operations in a transaction must succeed; otherwise, the entire transaction is rolled back. Money is debited from Account A AND credited to Account B. If power fails halfway, money is NOT lost.
C — Consistency Data must conform to all declared schema rules, constraints, and valid states before and after. Account balance cannot violate a CHECK (balance >= 0) constraint.
I — Isolation Concurrent transactions execute independently without interfering with each other's intermediate state. Two simultaneous ATM withdrawals don't collide or allow double-spending.
D — Durability Once a transaction is committed, changes are permanently written to non-volatile disk and survive server crashes. Server power outage after "Transaction Successful" will not erase the deposit.
2

SQL Basics: DDL, DML, DQL, and TCL Taxonomies

📌 What is SQL (Structured Query Language)?

SQL is the standard declarative programming language used to interact with relational databases. Instead of writing procedural loops to find records, you declare what you want, and the database query engine optimizes the retrieval path.

Sublanguage Full Name Primary Commands Purpose
DDL Data Definition Language CREATE, ALTER, DROP, TRUNCATE Defines or modifies table schemas and structures.
DML Data Manipulation Language INSERT, UPDATE, DELETE Modifies actual data records within tables.
DQL Data Query Language SELECT, WHERE, ORDER BY Retrieves and filters data records from tables.
TCL Transaction Control Language COMMIT, ROLLBACK, SAVEPOINT Manages atomic transaction boundaries.
3

Python Database Architecture: The DB-API 2.0 (PEP 249)

📌 The Universal Python Database Specification

Python standardizes all database drivers through PEP 249 (Python Database API Specification v2.0). Whether you connect to SQLite, MySQL, PostgreSQL, or Oracle, the underlying Python API remains identical:

  1. connect(): Opens a communication channel to the database file or remote server.
  2. Connection Object: Controls transaction state (commit(), rollback(), close()).
  3. Cursor Object: The active database pointer used to execute SQL statements (execute(), executemany()) and fetch result rows.
  4. Row Fetchers: fetchone() (retrieves single row), fetchall() (retrieves all matching rows as a list of tuples), and fetchmany(size).
📐 FLOWCHART 1: THE PYTHON DB-API 2.0 EXECUTION LIFECYCLE PEP 249 Standard
1. connect()
Establishes file handle or TCP connection socket.
➔ cursor() ➔
2. cursor.execute()
Executes parameterized SQL statement with data tuple.
➔ Save / Read ➔
3. commit() or fetchall()
Persists writes to disk OR extracts rows into Python tuples!
4

SQLite vs. MySQL: Serverless Embedded vs. Client-Server

📌 Architectural Comparison: SQLite vs. MySQL

• SQLite: A serverless, embedded database engine. There is no separate server daemon process running. The entire database is stored in a single cross-platform disk file (e.g., academy.db). It is built directly into Python's standard library (import sqlite3). Perfect for mobile apps, desktop tools, and development.
• MySQL: A client-server enterprise RDBMS. The database engine runs as an independent network daemon (typically on port 3306). Python connects across TCP/IP sockets using user authentication (username, password, host). Designed for large-scale web applications with thousands of concurrent users.

📐 FLOWCHART 2: ARCHITECTURE: EMBEDDED SQLITE VS. NETWORK MYSQL System Topography
🟢 SQLite (Embedded)
Python Process ➔ Directly reads/writes data.db file on disk. No network, no server setup!
VS
🔵 MySQL (Client-Server)
Python Client ➔ TCP/IP Socket (Port 3306) ➔ MySQL Daemon Server ➔ Database Storage.
📐 FLOWCHART 3: DEFENSIVE SECURITY: SQL INJECTION VS. PARAMETERIZED QUERIES OWASP Top 10 Defense
🔴 Vulnerable Concatenation (HACKABLE)
f"SELECT * FROM users WHERE name = '{user_input}'"
Malicious input ' OR '1'='1 bypasses authentication!
➔ FIXED BY ➔
🟢 Parameterized Placeholders (SECURE)
cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))
Database driver safely escapes and treats input purely as literal text!
5

The Full CRUD Operations Lifecycle in Python (SQLite)

📌 What is CRUD?

CRUD stands for the four fundamental persistent operations of any business data system:

  • C — Create: Initializing tables (CREATE TABLE) and adding records (INSERT INTO).
  • R — Read: Querying records (SELECT ... FROM ... WHERE) and fetching rows.
  • U — Update: Modifying existing records (UPDATE ... SET ... WHERE).
  • D — Delete: Removing records (DELETE FROM ... WHERE).
📐 FLOWCHART 4: THE 4 PHASES OF THE CRUD PERSISTENCE CYCLE Data State Transformations
1. CREATE
INSERT new student into database table.
➔
2. READ
SELECT student record to display profile.
➔
3. UPDATE
UPDATE marks after exam evaluation.
➔
4. DELETE
DELETE record upon student graduation.
🐍 01_sqlite_crud_complete.py
import sqlite3

# Step 1: Connect to SQLite (creates file if not present)
conn = sqlite3.connect("devascent_students.db")
cursor = conn.cursor()

# Step 2: CREATE Table
cursor.execute("""
CREATE TABLE IF NOT EXISTS students (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    course TEXT NOT NULL,
    gpa REAL NOT NULL
)
""")
conn.commit()

# Step 3: CREATE / INSERT Records using Parameterized Tuples (Secure against Injection)
students_batch = [
    ("Aman Sharma", "BCA", 8.4),
    ("Pooja Verma", "B.Tech CS", 9.2),
    ("Rahul Gupta", "BCA", 7.1)
]
cursor.executemany("INSERT INTO students (name, course, gpa) VALUES (?, ?, ?)", students_batch)
conn.commit()
print(f"[CREATE] Successfully inserted {cursor.rowcount} students.")

# Step 4: READ Records
cursor.execute("SELECT id, name, course, gpa FROM students WHERE gpa >= ?", (8.0,))
high_scorers = cursor.fetchall()
print("\n[READ] High Scorers (GPA >= 8.0):")
for row in high_scorers:
    print(f"  ID: {row[0]} | Name: {row[1]} | Course: {row[2]} | GPA: {row[3]}")

# Step 5: UPDATE Record
cursor.execute("UPDATE students SET gpa = ? WHERE name = ?", (7.8, "Rahul Gupta"))
conn.commit()
print(f"\n[UPDATE] Updated GPA for Rahul Gupta. Affected rows: {cursor.rowcount}")

# Step 6: DELETE Record
cursor.execute("DELETE FROM students WHERE name = ?", ("Aman Sharma",))
conn.commit()
print(f"[DELETE] Removed Aman Sharma. Affected rows: {cursor.rowcount}")

# Step 7: Clean close
cursor.close()
conn.close()
Terminal Output
[CREATE] Successfully inserted 3 students. [READ] High Scorers (GPA >= 8.0): ID: 1 | Name: Aman Sharma | Course: BCA | GPA: 8.4 ID: 2 | Name: Pooja Verma | Course: B.Tech CS | GPA: 9.2 [UPDATE] Updated GPA for Rahul Gupta. Affected rows: 1 [DELETE] Removed Aman Sharma. Affected rows: 1
6

Enterprise MySQL Connectivity (`mysql-connector-python`)

📌 Connecting to Remote MySQL Servers

To connect Python with an external MySQL server, install the official connector driver via:
pip install mysql-connector-python
Key syntax differences from SQLite:
• Connection requires credentials: host, user, password, and database.
• Parameter placeholders use %s instead of ?.

🐍 02_mysql_connector_example.py
import mysql.connector
from mysql.connector import Error

def connect_mysql_database():
    try:
        connection = mysql.connector.connect(
            host="localhost",
            port=3306,
            database="academy_db",
            user="root",
            password="DevAscent@2026"
        )
        if connection.is_connected():
            db_info = connection.get_server_info()
            print(f"Connected to MySQL Server Version: {db_info}")
            
            cursor = connection.cursor()
            cursor.execute("SELECT DATABASE();")
            active_db = cursor.fetchone()
            print(f"Active Connected Database: {active_db[0]}")
            
            # Executing parameterized query in MySQL using %s
            cursor.execute("SELECT name, email FROM employees WHERE dept_id = %s", (10,))
            records = cursor.fetchall()
            for emp in records:
                print(f"  Employee: {emp[0]} <{emp[1]}>")

    except Error as err:
        print(f"Database Connection Error: {err}")
    finally:
        if 'connection' in locals() and connection.is_connected():
            cursor.close()
            connection.close()
            print("MySQL connection successfully closed.")

# connect_mysql_database()
Terminal Output
Connected to MySQL Server Version: 8.0.35 Active Connected Database: academy_db Employee: Sarah Connor <s.connor@academy.org> Employee: John Doe <j.doe@academy.org> MySQL connection successfully closed.
7

Unit 15 Practical Capstone: Student Tuition Billing & Transaction Engine

This industrial capstone demonstrates ACID transaction reliability: processing a student tuition fee payment with a deduction from their bank balance and an update to their tuition ledger, automatically executing a ROLLBACK if any error occurs.

📐 FLOWCHART 5: DATABASE TRANSACTION STATE MACHINE (COMMIT VS. ROLLBACK) ACID Atomicity Engine
1. BEGIN TRANSACTION
Step A: Deduct tuition fee from account balance.
➔ Step B ➔
2. Record Ledger Entry
Creates audited receipt record with timestamp.
➔ Check ➔
3. All Passed? ➔ COMMIT
If error occurs ➔ ROLLBACK completely! No partial deductions.
🐍 tuition_billing_engine.py
import sqlite3

def init_billing_database():
    conn = sqlite3.connect(":memory:")  # In-memory database for ultra-fast sandbox testing
    cursor = conn.cursor()
    cursor.execute("""
    CREATE TABLE student_wallet (
        student_id INT PRIMARY KEY,
        name TEXT,
        balance REAL
    )
    """)
    cursor.execute("""
    CREATE TABLE payment_ledger (
        payment_id INTEGER PRIMARY KEY AUTOINCREMENT,
        student_id INT,
        amount REAL,
        status TEXT
    )
    """)
    cursor.execute("INSERT INTO student_wallet VALUES (501, 'Kavita Singh', 15000.0)")
    conn.commit()
    return conn

# Transactional Payment Function
def process_tuition_payment(conn, student_id, amount):
    cursor = conn.cursor()
    print(f"\nInitiating payment of ₹{amount:,.2f} for Student #{student_id}...")
    
    try:
        # Step 1: Check balance
        cursor.execute("SELECT balance FROM student_wallet WHERE student_id = ?", (student_id,))
        record = cursor.fetchone()
        
        if not record:
            raise ValueError("Student ID not found in system!")
            
        current_balance = record[0]
        if current_balance < amount:
            raise ValueError(f"Insufficient balance! Available: ₹{current_balance:,.2f}")

        # Step 2: Deduct from balance
        cursor.execute("UPDATE student_wallet SET balance = balance - ? WHERE student_id = ?", (amount, student_id))
        
        # Step 3: Record transaction in ledger
        cursor.execute("INSERT INTO payment_ledger (student_id, amount, status) VALUES (?, ?, ?)", 
                       (student_id, amount, "COMPLETED"))
        
        # Step 4: Commit transaction (Atomicity guaranteed)
        conn.commit()
        print("✅ [TRANSACTION SUCCESS] Tuition payment settled. Receipt generated.")

    except Exception as err:
        conn.rollback()  # Abort all operations; restore original state
        print(f"❌ [TRANSACTION FAILED] Reason: {err}")
        print("   [ROLLBACK] Reverted all database modifications to protect financial integrity.")

# Test Scenario 1: Successful Payment
db_conn = init_billing_database()
process_tuition_payment(db_conn, 501, 6500.0)

# Test Scenario 2: Overdraft Attempt (Triggers automatic ROLLBACK)
process_tuition_payment(db_conn, 501, 20000.0)

# Verification of final balance
cur = db_conn.cursor()
cur.execute("SELECT name, balance FROM student_wallet WHERE student_id = 501")
final_state = cur.fetchone()
print(f"\nFinal Audited Balance for {final_state[0]}: ₹{final_state[1]:,.2f}")
Terminal Output
Initiating payment of ₹6,500.00 for Student #501... ✅ [TRANSACTION SUCCESS] Tuition payment settled. Receipt generated. Initiating payment of ₹20,000.00 for Student #501... ❌ [TRANSACTION FAILED] Reason: Insufficient balance! Available: ₹8,500.00 [ROLLBACK] Reverted all database modifications to protect financial integrity. Final Audited Balance for Kavita Singh: ₹8,500.00
8

University Exam & Viva Preparation Guide (Frequently Asked Questions)

Q1. What is the role of the Cursor object in Python database connectivity?
The cursor is a control structure used to traverse and manipulate database records. It executes SQL commands (cursor.execute()) and acts as an iterator or fetch mechanism (fetchone(), fetchall()) to retrieve query results.
Q2. Why is `conn.commit()` mandatory after INSERT, UPDATE, or DELETE statements?
Python DB-API starts transactions automatically. Modifications exist only in temporary memory buffers until conn.commit() is explicitly called to permanently write the changes to the physical disk file or database engine.
Q3. What is SQL Injection and how do parameterized queries prevent it?
SQL Injection is a critical security vulnerability where an attacker injects malicious SQL fragments into input fields. Parameterized queries (using ? in SQLite or %s in MySQL) prevent this by treating input strictly as literal values rather than executable SQL code.
Q4. Differentiate between `fetchone()` and `fetchall()`.
cursor.fetchone() returns the next single matching record as a tuple, advancing the cursor by one row (or None if no rows remain).
cursor.fetchall() returns all remaining matching records at once as a Python list of tuples.
Q5. Explain the difference between SQLite and MySQL.
SQLite is serverless and embedded: the database is a local file on disk managed directly inside the Python process. MySQL is a client-server RDBMS running as a background network service communicating over TCP/IP (port 3306) with multi-user authorization.
Q6. What does `conn.rollback()` do?
conn.rollback() cancels and reverses all uncommitted database operations performed within the current transaction, restoring the database to its last committed state in case an error occurs.