From 4bc9b87317d1aea2a09a9e15ff7ac2842295588c Mon Sep 17 00:00:00 2001 From: AnantaSrikar Date: Sun, 27 Sep 2026 10:36:18 -0700 Subject: [PATCH] updated to mysql-connector-python --- README.md | 54 +++++++++++++++++++++--------------------------- config.py | 9 ++++++-- crud.py | 39 +++++++++++++++++----------------- db.py | 38 ++++++++++++++++++++++------------ main.py | 6 +++--- requirements.txt | 2 +- 6 files changed, 80 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 266c337..03256b2 100644 --- a/README.md +++ b/README.md @@ -6,41 +6,41 @@ Tables 9.3–9.5 (STUDENT, GUARDIAN, ATTENDANCE). ## What it does on startup -1. Connects to the MySQL server at `10.0.1.40:3306`. +1. Connects to the MySQL server at `10.10.0.160:3306`. 2. Verifies the `student_db` database exists — creates it if not. 3. Verifies the `GUARDIAN`, `STUDENT`, and `ATTENDANCE` tables exist - (in that order, since STUDENT references GUARDIAN and ATTENDANCE - references STUDENT) — creates any that are missing, with the exact - data types and constraints from the schema (primary keys, foreign - keys, `NOT NULL`, `UNIQUE` on `GPhone`, and the composite primary key - `(AttendanceDate, RollNumber)` on ATTENDANCE). + (in that order, since STUDENT references GUARDIAN and ATTENDANCE + references STUDENT) — creates any that are missing, with the exact + data types and constraints from the schema (primary keys, foreign + keys, `NOT NULL`, `UNIQUE` on `GPhone`, and the composite primary key + `(AttendanceDate, RollNumber)` on ATTENDANCE). 4. Launches an interactive menu for Create / Read / Update / Delete. ## Setup 1. Install the one dependency: - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` 2. Set your MySQL credentials as environment variables (the host/port/ - database default to the assignment's values already): + database default to the assignment's values already): - ```bash - export DB_USER=root - export DB_PASSWORD=your_password_here - # optional overrides — these already default correctly: - # export DB_HOST=10.0.1.40 - # export DB_PORT=3306 - # export DB_NAME=student_db - ``` + ```bash + export DB_USER=root + export DB_PASSWORD=your_password_here + # optional overrides — these already default correctly: + # export DB_HOST=10.10.0.160 + # export DB_PORT=3306 + # export DB_NAME=student_db + ``` 3. Run it: - ```bash - python3 main.py - ``` + ```bash + python3 main.py + ``` ## Files @@ -72,12 +72,6 @@ cascades and removes their attendance records automatically. ## Notes -- Uses [PyMySQL](https://pymysql.readthedocs.io/), a pure-Python MySQL - client — no system libraries (like `libmysqlclient`) required. -- All input is validated against the schema's constraints (max lengths, - digit counts, date format, P/A status) before hitting the database, - and database-level errors (duplicate keys, foreign key violations) - are caught and shown as friendly messages rather than crashing. -- Tested end-to-end (connect → create DB/tables → full CRUD → constraint - error handling → cascade delete) against a local MySQL/MariaDB - instance before delivery. +- Uses [mysql-connector-python](https://dev.mysql.com/doc/connector-python/en/), Oracle's official pure-Python MySQL driver — no system libraries (like `libmysqlclient`) required. +- All input is validated against the schema's constraints (max lengths, digit counts, date format, P/A status) before hitting the database, and database-level errors (duplicate keys, foreign key violations) are caught and shown as friendly messages rather than crashing. +- Tested end-to-end (connect → create DB/tables → full CRUD → constraint error handling → cascade delete) against a local MySQL/MariaDB instance before delivery. diff --git a/config.py b/config.py index 36fb2eb..16badb2 100644 --- a/config.py +++ b/config.py @@ -5,12 +5,17 @@ Connection settings are read from environment variables first, falling back to the defaults below (which match the assignment's requirements). This means you can run the app as-is, or override any value like: - DB_HOST=10.0.1.40 DB_USER=root DB_PASSWORD=secret python3 main.py + DB_HOST=10.10.0.160 DB_USER=root DB_PASSWORD=secret python3 main.py + + # on windows + $env:DB_USER="mysql_user" + $env:DB_PASSWORD="mysql_password" + python3 main.py """ import os -DB_HOST = os.environ.get("DB_HOST", "10.0.1.40") +DB_HOST = os.environ.get("DB_HOST", "10.10.0.160") DB_PORT = int(os.environ.get("DB_PORT", "3306")) DB_USER = os.environ.get("DB_USER", "root") DB_PASSWORD = os.environ.get("DB_PASSWORD", "") diff --git a/crud.py b/crud.py index 4518f05..add7282 100644 --- a/crud.py +++ b/crud.py @@ -1,12 +1,12 @@ """ CRUD operations for GUARDIAN, STUDENT and ATTENDANCE. -Every function takes an open pymysql connection as its first argument and -manages its own commit/rollback. All functions return plain data -(dicts/lists/bool) so this module has no dependency on the UI layer. +Every function takes an open mysql-connector-python connection as its first +argument and manages its own commit/rollback. All functions return plain +data (dicts/lists/bool) so this module has no dependency on the UI layer. """ -import pymysql +import mysql.connector class CrudError(Exception): @@ -20,33 +20,34 @@ def _run(conn, sql, params=None, fetch=None): fetch: None (no fetch), 'one', or 'all' Translates common MySQL errors into CrudError with a friendly message. """ + cur = conn.cursor(dictionary=True) try: - with conn.cursor() as cur: - cur.execute(sql, params or ()) - result = None - if fetch == "one": - result = cur.fetchone() - elif fetch == "all": - result = cur.fetchall() - conn.commit() - return result if fetch else cur.rowcount - except pymysql.err.IntegrityError as e: + cur.execute(sql, params or ()) + result = None + if fetch == "one": + result = cur.fetchone() + elif fetch == "all": + result = cur.fetchall() + conn.commit() + return result if fetch else cur.rowcount + except mysql.connector.errors.IntegrityError as e: conn.rollback() - code = e.args[0] if e.args else None - if code == 1062: + if e.errno == 1062: raise CrudError("A record with that primary/unique key already exists.") - if code in (1451, 1452): + if e.errno in (1451, 1452): raise CrudError( "That operation violates a foreign key constraint " "(referenced record missing, or still referenced by other rows)." ) raise CrudError(f"Database integrity error: {e}") - except pymysql.err.DataError as e: + except mysql.connector.errors.DataError as e: conn.rollback() raise CrudError(f"Invalid data for one or more fields: {e}") - except pymysql.err.MySQLError as e: + except mysql.connector.Error as e: conn.rollback() raise CrudError(f"Database error: {e}") + finally: + cur.close() # ---------------------------------------------------------------- GUARDIAN -- diff --git a/db.py b/db.py index 62adea1..c589b26 100644 --- a/db.py +++ b/db.py @@ -13,8 +13,8 @@ Schema notes: import sys -import pymysql -import pymysql.cursors +import mysql.connector +from mysql.connector import errorcode import config @@ -61,20 +61,25 @@ TABLE_ORDER = ["GUARDIAN", "STUDENT", "ATTENDANCE"] def connect_server(database=None): """Connect to the MySQL server (optionally selecting a database).""" try: - conn = pymysql.connect( + conn = mysql.connector.connect( host=config.DB_HOST, port=config.DB_PORT, user=config.DB_USER, password=config.DB_PASSWORD, database=database, - connect_timeout=config.DB_CONNECT_TIMEOUT, - cursorclass=pymysql.cursors.DictCursor, + connection_timeout=config.DB_CONNECT_TIMEOUT, autocommit=False, ) return conn - except pymysql.err.OperationalError as e: - print(f"\n[ERROR] Could not connect to MySQL server at " - f"{config.DB_HOST}:{config.DB_PORT} as user '{config.DB_USER}'.") + except mysql.connector.Error as e: + if e.errno == errorcode.ER_ACCESS_DENIED_ERROR: + print(f"\n[ERROR] Access denied for user '{config.DB_USER}' — check " + f"DB_USER/DB_PASSWORD.") + elif database and e.errno == errorcode.ER_BAD_DB_ERROR: + print(f"\n[ERROR] Database '{database}' does not exist.") + else: + print(f"\n[ERROR] Could not connect to MySQL server at " + f"{config.DB_HOST}:{config.DB_PORT} as user '{config.DB_USER}'.") print(f" Details: {e}") sys.exit(1) @@ -83,7 +88,8 @@ def ensure_database(): """Verify the student_db database exists; create it if it does not.""" conn = connect_server(database=None) try: - with conn.cursor() as cur: + cur = conn.cursor() + try: cur.execute("SHOW DATABASES LIKE %s", (config.DB_NAME,)) exists = cur.fetchone() is not None if exists: @@ -96,23 +102,29 @@ def ensure_database(): ) conn.commit() print(f"[OK] Database '{config.DB_NAME}' created.") + finally: + cur.close() finally: conn.close() def ensure_tables(conn): """Verify each required table exists; create any that are missing.""" - with conn.cursor() as cur: - cur.execute("SHOW TABLES") - existing = {row[f"Tables_in_{config.DB_NAME}"] for row in cur.fetchall()} + cur = conn.cursor() + cur.execute("SHOW TABLES") + existing = {row[0] for row in cur.fetchall()} + cur.close() for table in TABLE_ORDER: if table in existing: print(f"[OK] Table '{table}' found.") else: print(f"[..] Table '{table}' not found. Creating it...") - with conn.cursor() as cur: + cur = conn.cursor() + try: cur.execute(TABLE_DEFINITIONS[table]) + finally: + cur.close() conn.commit() print(f"[OK] Table '{table}' created.") diff --git a/main.py b/main.py index 17e05ef..d33d616 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ Student Management System ========================== Terminal CRUD application for STUDENT / GUARDIAN / ATTENDANCE, backed by -MySQL on 10.0.1.40:3306 (student_db). +MySQL on 10.10.0.160:3306 (student_db). On startup this: 1. Connects to the MySQL server. @@ -17,7 +17,7 @@ Run: import sys -import pymysql +import mysql.connector import crud import db @@ -326,7 +326,7 @@ def main(): main_menu(conn) except KeyboardInterrupt: print("\n\nInterrupted. Exiting.") - except pymysql.err.MySQLError as e: + except mysql.connector.Error as e: print(f"\n[ERROR] Lost database connection: {e}") sys.exit(1) finally: diff --git a/requirements.txt b/requirements.txt index 632cb38..ae52c74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -PyMySQL>=1.1.0 +mysql-connector-python>=9.0.0