diff --git a/semester-3/python-iii/flask-231206/.python-version b/semester-3/python-iii/flask-231206/.python-version new file mode 100644 index 0000000..e3b6664 --- /dev/null +++ b/semester-3/python-iii/flask-231206/.python-version @@ -0,0 +1 @@ +cpython-aarch64-macos@3.12.0 diff --git a/semester-3/python-iii/flask-231206/README.md b/semester-3/python-iii/flask-231206/README.md new file mode 100644 index 0000000..1be603d --- /dev/null +++ b/semester-3/python-iii/flask-231206/README.md @@ -0,0 +1,3 @@ +# mysqlexample + +Describe your project here. diff --git a/semester-3/python-iii/flask-231206/pyproject.toml b/semester-3/python-iii/flask-231206/pyproject.toml new file mode 100644 index 0000000..bda9e13 --- /dev/null +++ b/semester-3/python-iii/flask-231206/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "mysqlexample" +version = "0.1.0" +description = "Add your description here" +authors = [ + { name = "pan93412", email = "pan93412@gmail.com" } +] +dependencies = [ + "sqlalchemy>=2.0.23", + "mysql-connector-python>=8.2.0", +] +readme = "README.md" +requires-python = ">= 3.8" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.rye] +managed = true +dev-dependencies = [ + "black>=23.11.0", +] + +[tool.hatch.metadata] +allow-direct-references = true diff --git a/semester-3/python-iii/flask-231206/requirements-dev.lock b/semester-3/python-iii/flask-231206/requirements-dev.lock new file mode 100644 index 0000000..75fde40 --- /dev/null +++ b/semester-3/python-iii/flask-231206/requirements-dev.lock @@ -0,0 +1,19 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: false + +-e file:. +black==23.11.0 +click==8.1.7 +mypy-extensions==1.0.0 +mysql-connector-python==8.2.0 +packaging==23.2 +pathspec==0.11.2 +platformdirs==4.1.0 +protobuf==4.21.12 +sqlalchemy==2.0.23 +typing-extensions==4.8.0 diff --git a/semester-3/python-iii/flask-231206/requirements.lock b/semester-3/python-iii/flask-231206/requirements.lock new file mode 100644 index 0000000..45d1521 --- /dev/null +++ b/semester-3/python-iii/flask-231206/requirements.lock @@ -0,0 +1,13 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: false + +-e file:. +mysql-connector-python==8.2.0 +protobuf==4.21.12 +sqlalchemy==2.0.23 +typing-extensions==4.8.0 diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/01-create-db-connector.py b/semester-3/python-iii/flask-231206/src/mysqlexample/01-create-db-connector.py new file mode 100644 index 0000000..222f8c9 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/01-create-db-connector.py @@ -0,0 +1,17 @@ +import os +from mysql.connector import connection + +# Connect to MySQL server +with connection.MySQLConnection(user='root', password=os.environ["MYSQL_PASSWORD"], + host='localhost') as cnx: + cursor = cnx.cursor() + + # Get the version of the MySQL server + cursor.execute("SELECT VERSION()") + result = cursor.fetchone() + print(result) + + # Create a database if not exist. + cursor.execute("CREATE DATABASE IF NOT EXISTS example_c DEFAULT CHARSET utf8 COLLATE utf8_general_ci") + + cnx.commit() diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/01-create-db-sqlalchemy.py b/semester-3/python-iii/flask-231206/src/mysqlexample/01-create-db-sqlalchemy.py new file mode 100644 index 0000000..e1ef229 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/01-create-db-sqlalchemy.py @@ -0,0 +1,14 @@ +import os +from sqlalchemy import String, create_engine, select, text, func + +# Create a connection to the database +engine = create_engine(f'mysql+mysqlconnector://root:{os.environ["MYSQL_PASSWORD"]}@localhost') + +with engine.connect() as connection: + # Get the current version of the MySQL server + result = connection.execute(select(func.version(type_=String()))) + print(result.first()) + + # Create a database if not exist. + connection.execute(text("CREATE DATABASE IF NOT EXISTS example_sa DEFAULT CHARSET utf8 COLLATE utf8_general_ci")) + connection.commit() diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/02-create-table-connector.py b/semester-3/python-iii/flask-231206/src/mysqlexample/02-create-table-connector.py new file mode 100644 index 0000000..681f5a7 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/02-create-table-connector.py @@ -0,0 +1,24 @@ +import os +from mysql.connector import connection +# Connect to MySQL server +with connection.MySQLConnection(user='root', password=os.environ["MYSQL_PASSWORD"], + host='localhost', database='example_c') as cnx: + + cursor = cnx.cursor() + + # Create a stu_data table, with: + # stu_no as primary key + # stu_name + # stu_class + # stu_phone + + # Create a table if not exist. + cursor.execute(( + "CREATE TABLE IF NOT EXISTS stu_data (" + "stu_no VARCHAR(10) PRIMARY KEY," + "stu_name VARCHAR(255) NOT NULL," + "stu_class VARCHAR(255) NOT NULL," + "stu_phone VARCHAR(10) NOT NULL)" + )) + + cnx.commit() diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/03-insert-data-connector.py b/semester-3/python-iii/flask-231206/src/mysqlexample/03-insert-data-connector.py new file mode 100644 index 0000000..0b64370 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/03-insert-data-connector.py @@ -0,0 +1,35 @@ +import os +from mysql.connector import connection +# Connect to MySQL server +with connection.MySQLConnection(user='root', password=os.environ["MYSQL_PASSWORD"], + host='localhost', database='example_c') as cnx: + cursor = cnx.cursor() + + # Insert data into stu_data table + cursor.executemany( + "INSERT INTO stu_data (stu_no, stu_name, stu_class, stu_phone) VALUES (%s, %s, %s, %s)", + [ + # 15 mock data + ("C111156101", "王小明", "智商二甲", "0912345678"), + ("C111156102", "陳小華", "智商二甲", "0913579246"), + ("C111156103", "林小美", "智商二甲", "0912345678"), + ("C111156104", "張小強", "智商二甲", "0913579246"), + ("C111156105", "李大雄", "智商二甲", "0912345678"), + ("C111156106", "黃小琪", "智商二甲", "0913579246"), + ("C111156107", "吳小菁", "智商二甲", "0912345678"), + ("C111156108", "劉小偉", "智商二甲", "0913579246"), + ("C111156109", "蔡小娟", "智商二甲", "0912345678"), + ("C111156110", "許小儒", "智商二甲", "0913579246"), + ("C111156111", "林小雨", "智商二甲", "0912345678"), + ("C111156112", "陳小風", "智商二甲", "0913579246"), + ("C111156113", "黃小雲", "智商二甲", "0912345678"), + ("C111156114", "張小翰", "智商二甲", "0913579246"), + ("C111156115", "王小明", "智商二甲", "0912345678"), + ] + ) + cnx.commit() + + # Get all datas from stu_data table + cursor.execute("SELECT * FROM stu_data") + result = cursor.fetchall() + print(result) diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/03-insert-data-sqlalchemy.py b/semester-3/python-iii/flask-231206/src/mysqlexample/03-insert-data-sqlalchemy.py new file mode 100644 index 0000000..d3337b5 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/03-insert-data-sqlalchemy.py @@ -0,0 +1,51 @@ +import os +from sqlalchemy import Column, MetaData, String, Table, create_engine, insert, select + +# Create a connection to the database +engine = create_engine(f'mysql+mysqlconnector://root:{os.environ["MYSQL_PASSWORD"]}@localhost/example_sa') + +# Create a metadata with the tables. +metadata = MetaData() + +# Create a stu_data table, with: +# stu_no as primary key +# stu_name +# stu_class +# stu_phone +Student = Table( + "students", + metadata, + Column("stu_no", String(10), primary_key=True), + Column("stu_name", String(255), nullable=False), + Column("stu_class", String(255), nullable=False), + Column("stu_phone", String(10), nullable=False), +) + +with engine.connect() as connection: + # Create a table if not exist. + metadata.create_all(engine) + + # Insert data into stu_data table + stmt = insert(Student).values([ + {"stu_no": "C111156101", "stu_name": "王小明", "stu_class": "智商二甲", "stu_phone": "0912345678"}, + {"stu_no": "C111156102", "stu_name": "陳小華", "stu_class": "智商二甲", "stu_phone": "0913579246"}, + {"stu_no": "C111156103", "stu_name": "林小美", "stu_class": "智商二甲", "stu_phone": "0912345678"}, + {"stu_no": "C111156104", "stu_name": "張小強", "stu_class": "智商二甲", "stu_phone": "0913579246"}, + {"stu_no": "C111156105", "stu_name": "李大雄", "stu_class": "智商二甲", "stu_phone": "0912345678"}, + {"stu_no": "C111156106", "stu_name": "黃小琪", "stu_class": "智商二甲", "stu_phone": "0913579246"}, + {"stu_no": "C111156107", "stu_name": "吳小菁", "stu_class": "智商二甲", "stu_phone": "0912345678"}, + {"stu_no": "C111156108", "stu_name": "劉小偉", "stu_class": "智商二甲", "stu_phone": "0913579246"}, + {"stu_no": "C111156109", "stu_name": "蔡小娟", "stu_class": "智商二甲", "stu_phone": "0912345678"}, + {"stu_no": "C111156110", "stu_name": "許小儒", "stu_class": "智商二甲", "stu_phone": "0913579246"}, + {"stu_no": "C111156111", "stu_name": "林小雨", "stu_class": "智商二甲", "stu_phone": "0912345678"}, + {"stu_no": "C111156112", "stu_name": "陳小風", "stu_class": "智商二甲", "stu_phone": "0913579246"}, + ]) + result = connection.execute(stmt) + print("Inserted %d rows" % result.rowcount) + connection.commit() + + # Get all datas from stu_data table + stmt = select(Student) + result = connection.execute(stmt) + print(result.fetchall()) + diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/04-select-data-connector.py b/semester-3/python-iii/flask-231206/src/mysqlexample/04-select-data-connector.py new file mode 100644 index 0000000..448123f --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/04-select-data-connector.py @@ -0,0 +1,15 @@ +import os +from mysql.connector import connection +# Connect to MySQL server +with connection.MySQLConnection(user='root', password=os.environ["MYSQL_PASSWORD"], + host='localhost', database='example_c') as cnx: + cursor = cnx.cursor() + + # 輸入一個學號,查詢該學生的資料 + stu_no = input("請輸入學號: ") + + # 從資料庫查詢特定資料 + cursor.execute("SELECT * FROM stu_data WHERE stu_no = %s", (stu_no,)) + + result = cursor.fetchone() + print(result) diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/04-select-data-sqlalchemy.py b/semester-3/python-iii/flask-231206/src/mysqlexample/04-select-data-sqlalchemy.py new file mode 100644 index 0000000..b05a500 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/04-select-data-sqlalchemy.py @@ -0,0 +1,28 @@ +import os +from sqlalchemy import Column, MetaData, String, Table, create_engine, insert, select +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session + +# Create a connection to the database +engine = create_engine(f'mysql+mysqlconnector://root:{os.environ["MYSQL_PASSWORD"]}@localhost/example_sa') + + +class Base(DeclarativeBase): pass + +class Student(Base): + __tablename__ = "students" + + stu_no: Mapped[str] = mapped_column(String(10), primary_key=True) + stu_name: Mapped[str] = mapped_column(String(255), nullable=False) + stu_class: Mapped[str] = mapped_column(String(255), nullable=False) + stu_phone: Mapped[str] = mapped_column(String(10), nullable=False) + + def __repr__(self): + return f"" + +with Session(engine) as session: + # Ask user to input a student number + stu_no = input("請輸入學號: ") + + student = session.get(Student, stu_no) + + print(student if student else "Not found.") diff --git a/semester-3/python-iii/flask-231206/src/mysqlexample/05-update-data-with-gui.py b/semester-3/python-iii/flask-231206/src/mysqlexample/05-update-data-with-gui.py new file mode 100644 index 0000000..88f59a0 --- /dev/null +++ b/semester-3/python-iii/flask-231206/src/mysqlexample/05-update-data-with-gui.py @@ -0,0 +1,112 @@ +import os +from sqlalchemy import String, create_engine +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session +import tkinter +import tkinter.messagebox +from tkinter.ttk import Button, Entry, Frame, Label + + +class Base(DeclarativeBase): pass + +class Student(Base): + __tablename__ = "students" + + stu_no: Mapped[str] = mapped_column(String(10), primary_key=True) + stu_name: Mapped[str] = mapped_column(String(255), nullable=False) + stu_class: Mapped[str] = mapped_column(String(255), nullable=False) + stu_phone: Mapped[str] = mapped_column(String(10), nullable=False) + + def __repr__(self): + return f"" + +# Create a tkinter window +window = tkinter.Tk() +window.title("查詢並修改學生資料") + +# Create a connection to the database +engine = create_engine(f'mysql+mysqlconnector://root:{os.environ["MYSQL_PASSWORD"]}@localhost/example_sa') +session = Session(engine) + +# Allow users to input a student number +id_input_frame = Frame(window) +id_input_frame.pack(padx=10, pady=10) + +id_input_entry_frame = Frame(id_input_frame) +id_input_entry_frame.pack() +stu_no_label = Label(id_input_entry_frame, text="學號") +stu_no_label.grid(row=0, column=0) +stu_no_entry = Entry(id_input_entry_frame) +stu_no_entry.grid(row=0, column=1) +stu_query_btn = Button(id_input_frame, text="查詢") +stu_query_btn.pack() + +# Show the student data +stu_data_frame = Frame(window) +stu_data_frame.pack(padx=10, pady=10) +stu_input_frame = Frame(stu_data_frame) +stu_input_frame.pack() +stu_name_label = Label(stu_input_frame, text="姓名") +stu_name_label.grid(row=0, column=0) +stu_name_entry = Entry(stu_input_frame) +stu_name_entry.grid(row=0, column=1) +stu_class_label = Label(stu_input_frame, text="班級") +stu_class_label.grid(row=1, column=0) +stu_class_entry = Entry(stu_input_frame) +stu_class_entry.grid(row=1, column=1) +stu_phone_label = Label(stu_input_frame, text="電話") +stu_phone_label.grid(row=2, column=0) +stu_phone_entry = Entry(stu_input_frame) +stu_phone_entry.grid(row=2, column=1) +stu_modify_btn_frame = Frame(stu_data_frame) +stu_modify_btn_frame.pack() +stu_update_btn = Button(stu_modify_btn_frame, text="修改") +stu_update_btn.grid(row=0, column=0) + + +current_selected_student: str | None = None + +def select() -> None: + global current_selected_student + + current_selected_student = stu_no_entry.get() + student = session.get(Student, current_selected_student) + + # Write student's value to the entry + if student: + stu_name_entry.delete(0, tkinter.END) + stu_name_entry.insert(0, student.stu_name) + stu_class_entry.delete(0, tkinter.END) + stu_class_entry.insert(0, student.stu_class) + stu_phone_entry.delete(0, tkinter.END) + stu_phone_entry.insert(0, student.stu_phone) + else: + tkinter.messagebox.showinfo("查詢", "查無此學生") + current_selected_student = None + + +def update() -> None: + global current_selected_student + + if current_selected_student is None: + tkinter.messagebox.showerror("修改", "請先查詢學生") + return + + student = session.get(Student, current_selected_student) + + try: + assert student is not None + student.stu_name = stu_name_entry.get() + student.stu_class = stu_class_entry.get() + student.stu_phone = stu_phone_entry.get() + session.commit() + tkinter.messagebox.showinfo("修改", "修改成功") + except AssertionError: + tkinter.messagebox.showerror("修改", "查無此學生") + except SQLAlchemyError as e: + tkinter.messagebox.showerror("修改", f"修改失敗: {e}") + +stu_query_btn.bind("", lambda event: select()) +stu_update_btn.bind("", lambda event: update()) + +window.mainloop()