SQL:
create table School # Create table( `SchoolId` char(5) NOT NULL comment 'Primary key, school number', # Manually input, can also be designed to auto-increment, 5 characters, numeric, or a combination of numeric and characters, with code restrictions when saving data in text. `SchoolName` nvarchar(500) NOT NULL DEFAULT '' comment 'School name', `SchoolTelNo` varchar(8) NULL DEFAULT '' comment 'Phone number', PRIMARY KEY (`SchoolId`) # Primary key)ENGINE=MyISAM COMMENT='School Table' DEFAULT CHARSET=utf8; create table Teacher # Create table( `TeacherId` char(5) NOT NULL comment 'Primary key, teacher number', `TeacherFirstName` nvarchar(100) NOT NULL DEFAULT '' comment 'First name', `TeacherLastName` nvarchar(20) NOT NULL DEFAULT '' comment 'Last name', `TeacherGender` char(2) NOT NULL DEFAULT '' comment 'Gender', `TeacherTelNo` varchar(8) NULL DEFAULT '' comment 'Phone number', `TeacherSchoolId` char(5) NOT NULL DEFAULT '' comment 'Foreign key, school ID', PRIMARY KEY (`TeacherId`), # Primary key CONSTRAINT TeacherSchool_ibfk_1 FOREIGN KEY(TeacherSchoolId) REFERENCES School(SchoolId) # Foreign key)ENGINE=MyISAM COMMENT='Teacher Table' DEFAULT CHARSET=utf8;
Data processing layers can be handled as mentioned in the previous article. Justreplace the presenter (MVP) with PyQt6.
Project Structure:
# encoding: utf-8# Copyright 2025 © Geovin Du Ltd.™# License information: Language has become a contributor to merit and responsibility, do we still need to work daily?# Description:# Author : geovindu, Geovin Du.# IDE : PyCharm 2023.1 python 3.11# OS : Windows 10# Database : MySQL 9.0 SQL Server 2019, PostgreSQL 17.0 Oracle 21c Neo4j# Datetime : 2025/3/29 22:51# User : geovindu# Product : PyCharm# Project : pymysqlDDDQt# File : school.py# Explain : Learningimport sysfrom PyQt6.QtWidgets import QApplication, QTabWidgetfrom presentation.controllers.school import SchoolControllerfrom presentation.views.school import SchoolView
class SchoolPresenter: """ """ def __init__(self, view, controller: SchoolController): """ :param view: :param controller: """ self.view = view self.controller = controller self.current_page = 1 self.page_size = 10 self.search_query = "" self.setup_connections() self.update_table()
def setup_connections(self): """ :return: """ self.view.search_button.clicked.connect(self.on_search) self.view.add_button.clicked.connect(self.on_add) self.view.edit_button.clicked.connect(self.on_edit) self.view.delete_button.clicked.connect(self.on_delete) self.view.prev_button.clicked.connect(self.on_prev_page) self.view.next_button.clicked.connect(self.on_next_page)
def update_table(self): """ :return: """ try: schools = self.controller.get_all_schools( self.current_page, self.page_size, self.search_query) print(schools) self.view.set_table_data(schools) total_records = self.controller.get_total_schools(self.search_query) print(total_records) total_pages = (total_records + self.page_size - 1) // self.page_size self.view.set_pagination_info(self.current_page, total_pages, total_records) except Exception as e: print(f"Error updating school table data: {e}")
def on_search(self): """ :return: """ self.search_query = self.view.get_search_query() self.current_page = 1 self.update_table()
def on_add(self): """ :return: """ try: school = self.view.show_add_dialog() if school: self.controller.add_school(school) self.update_table() self.view.show_message("School added successfully.") except Exception as e: print(f"Error adding school: {e}")
def on_edit(self): """ :return: """ try: row = self.view.get_selected_row() if row != -1: school_id = self.view.table.item(row, 0).text() school = self.controller.get_all_schools( self.current_page, self.page_size, self.search_query)[row] edited_school = self.view.show_edit_dialog(school) if edited_school: edited_school.SchoolId = school_id self.controller.update_school(edited_school) self.update_table() self.view.show_message("School updated successfully.") else: self.view.show_message("Please select a school to edit.") except Exception as e: print(f"Error editing school: {e}")
def on_delete(self): """ :return: """ try: row = self.view.get_selected_row() if row != -1: school_id = self.view.table.item(row, 0).text() self.controller.delete_school(school_id) self.update_table() self.view.show_message("School deleted successfully.") else: self.view.show_message("Please select a school to delete.") except Exception as e: print(f"Error deleting school: {e}")
def on_prev_page(self): """ :return: """ if self.current_page > 1: self.current_page -= 1 self.update_table()
def on_next_page(self): """ :return: """ total_schools = self.controller.get_total_schools(self.search_query) total_pages = (total_schools + self.page_size - 1) // self.page_size if self.current_page < total_pages: self.current_page += 1 self.update_table()
# encoding: utf-8# Copyright 2025 © Geovin Du Ltd.™# License information: Language has become a contributor to merit and responsibility, do we still need to work daily?# Description:# Author : geovindu, Geovin Du.# IDE : PyCharm 2023.1 python 3.11# OS : Windows 10# Database : MySQL 9.0 SQL Server 2019, PostgreSQL 17.0 Oracle 21c Neo4j# Datetime : 2025/3/29 22:51# User : geovindu# Product : PyCharm# Project : pymysqlDDDQt# File : teacher.py# Explain : Learningfrom presentation.controllers.teacher import TeacherControllerfrom presentation.views.teacher import TeacherViewfrom presentation.controllers.school import SchoolController
class TeacherPresenter: """
""" def __init__(self, view, controller: TeacherController, scontroller: SchoolController): self.view = view self.controller = controller self.scontroller = scontroller self.school_map = None self.current_page = 1 self.page_size = 10 self.search_query = "" self.setup_connections() self.update_table()
def setup_connections(self): """ :return: """ self.view.search_button.clicked.connect(self.on_search) self.view.add_button.clicked.connect(self.on_add) self.view.edit_button.clicked.connect(self.on_edit) self.view.delete_button.clicked.connect(self.on_delete) self.view.prev_button.clicked.connect(self.on_prev_page) self.view.next_button.clicked.connect(self.on_next_page) # School selection schools = self.scontroller.get_schoolall() print("schools:") print(schools) # This has a syntax error self.school_map = schools # {s.school_name: s.school_id for s in schools} def update_table(self): """ :return: """ try: teachers = self.controller.get_all_teachers( self.current_page, self.page_size, self.search_query) self.view.set_table_data(teachers) total_records = self.controller.get_total_teachers(self.search_query) total_pages = (total_records + self.page_size - 1) // self.page_size self.view.set_pagination_info(self.current_page, total_pages, total_records) except Exception as e: print(f"Error updating teacher table data: {e}")
def on_search(self): """ :return: """ self.search_query = self.view.get_search_query() self.current_page = 1 self.update_table()
def on_add(self): """ :return: """ try: teacher = self.view.show_add_dialog(self.school_map) if teacher: self.controller.add_teacher(teacher) self.update_table() self.view.show_message("Teacher added successfully.") except Exception as e: print(f"Error adding teacher: {e}")
def on_edit(self): """ :return: """ try: row = self.view.get_selected_row() if row != -1: teacher_id = self.view.table.item(row, 0).text() teacher = self.controller.get_all_teachers( self.current_page, self.page_size, self.search_query)[row] # edited_teacher = self.view.show_edit_dialog(teacher,self.school_map) if edited_teacher: edited_teacher.TeacherId = teacher_id self.controller.update_teacher(edited_teacher) self.update_table() self.view.show_message("Teacher updated successfully.") else: self.view.show_message("Please select a teacher to edit.") except Exception as e: print(f"Error editing teacher: {e}")
def on_delete(self): """ :return: """ try: row = self.view.get_selected_row() if row != -1: teacher_id = self.view.table.item(row, 0).text() self.controller.delete_teacher(teacher_id) self.update_table() self.view.show_message("Teacher deleted successfully.") else: self.view.show_message("Please select a teacher to delete.") except Exception as e: print(f"Error deleting teacher: {e}")
def on_prev_page(self): """ :return: """ if self.current_page > 1: self.current_page -= 1 self.update_table()
def on_next_page(self): """ :return: """ total_teachers = self.controller.get_total_teachers(self.search_query) total_pages = (total_teachers + self.page_size - 1) // self.page_size if self.current_page < total_pages: self.current_page += 1 self.update_table()
# encoding: utf-8# Copyright 2025 © Geovin Du Ltd.™# License information: Language has become a contributor to merit and responsibility, do we still need to work daily?# Description:# Author : geovindu, Geovin Du.# IDE : PyCharm 2023.1 python 3.11# OS : Windows 10# Database : MySQL 9.0 SQL Server 2019, PostgreSQL 17.0 Oracle 21c Neo4j# Datetime : 2025/3/29 21:34# User : geovindu# Product : PyCharm# Project : pymysqlDDDQt# File : school.py# Explain : Learningfrom PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QPushButton, QLabel, QLineEdit, QDialog, QFormLayout, QDialogButtonBox, QMessageBox, QMdiArea, QMdiSubWindow)from domain.entities.school import School
class SchoolView(QWidget): """ """ def __init__(self): """ """ super().__init__() self.initUI()
def initUI(self): """ :return: """ self.mdi_area = QMdiArea() self.sub_window = QMdiSubWindow() self.main_widget = QWidget()
self.table = QTableWidget() self.table.setColumnCount(3) self.table.setHorizontalHeaderLabels( ['SchoolId', 'SchoolName', 'SchoolTelNo'])
self.search_input = QLineEdit() self.search_button = QPushButton("Search") self.add_button = QPushButton("Add") self.edit_button = QPushButton("Edit") self.delete_button = QPushButton("Delete") self.prev_button = QPushButton("Prev") self.next_button = QPushButton("Next")
self.pagination_label = QLabel() search_layout = QHBoxLayout() search_layout.addWidget(self.search_input) search_layout.addWidget(self.search_button)
button_layout = QHBoxLayout() button_layout.addWidget(self.add_button) button_layout.addWidget(self.edit_button) button_layout.addWidget(self.delete_button) button_layout.addWidget(self.prev_button) button_layout.addWidget(self.next_button) button_layout.addWidget(self.pagination_label)
layout = QVBoxLayout() layout.addLayout(search_layout) layout.addWidget(self.table) layout.addLayout(button_layout)
self.main_widget.setLayout(layout) self.sub_window.setWidget(self.main_widget) self.mdi_area.addSubWindow(self.sub_window)
main_layout = QVBoxLayout() main_layout.addWidget(self.mdi_area) self.setLayout(main_layout)
def set_table_data(self, schools): """ :param schools: :return: """ self.table.setRowCount(len(schools))
for row, school in enumerate(schools): self.table.setItem(row, 0, QTableWidgetItem(school.school_id)) self.table.setItem(row, 1, QTableWidgetItem(school.school_name)) self.table.setItem(row, 2, QTableWidgetItem(school.school_tel_no))
def get_selected_row(self): """ :return: """ selected_items = self.table.selectedItems() if selected_items: return selected_items[0].row() return -1
def get_search_query(self): """ :return: """ return self.search_input.text()
def show_add_dialog(self): """ :return: """ dialog = QDialog(self) dialog.setWindowTitle("Add School") layout = QFormLayout()
id_input = QLineEdit() name_input = QLineEdit() tel_input = QLineEdit()
layout.addRow("School ID:", id_input) layout.addRow("School Name:", name_input) layout.addRow("School Tel No:", tel_input)
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) layout.addWidget(button_box)
dialog.setLayout(layout)
if dialog.exec() == QDialog.DialogCode.Accepted: school_id = id_input.text() name = name_input.text() tel_no = tel_input.text() return School(SchoolId=school_id, SchoolName=name, SchoolTelNo=tel_no) return None
def show_edit_dialog(self, school): """ :param school: :return: """ dialog = QDialog(self) dialog.setWindowTitle("Edit School") layout = QFormLayout()
id_input = QLineEdit(school.school_id) name_input = QLineEdit(school.school_name) tel_input = QLineEdit(school.school_tel_no)
layout.addRow("School ID:", id_input) layout.addRow("School Name:", name_input) layout.addRow("School Tel No:", tel_input)
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) layout.addWidget(button_box)
dialog.setLayout(layout)
if dialog.exec() == QDialog.DialogCode.Accepted: school.SchoolId = id_input.text() school.SchoolName = name_input.text() school.SchoolTelNo = tel_input.text() return school return None
def show_message(self, message): """ :param message: :return: """ QMessageBox.information(self, 'Message', message)
def set_pagination_info(self, current_page, total_pages, total_records): """ :param current_page: :param total_pages: :param total_records: :return: """ info_text = f"Current page: {current_page} / Total pages: {total_pages} Total records: {total_records}" self.pagination_label.setText(info_text)
# encoding: utf-8# Copyright 2025 © Geovin Du Ltd.™# License information: Language has become a contributor to merit and responsibility, do we still need to work daily?# Description:# Author : geovindu, Geovin Du.# IDE : PyCharm 2023.1 python 3.11# OS : Windows 10# Database : MySQL 9.0 SQL Server 2019, PostgreSQL 17.0 Oracle 21c Neo4j# Datetime : 2025/3/29 21:34# User : geovindu# Product : PyCharm# Project : pymysqlDDDQt# File : teacher.py# Explain : Learningfrom PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem, QPushButton, QLabel, QLineEdit, QDialog, QFormLayout, QDialogButtonBox, QMessageBox, QMdiArea, QMdiSubWindow, QComboBox)from domain.entities.teacher import Teacher
class TeacherView(QWidget): """ """ def __init__(self): """ """ super().__init__() self.initUI()
def initUI(self): """ :return: """ self.mdi_area = QMdiArea() self.sub_window = QMdiSubWindow() self.main_widget = QWidget()
self.table = QTableWidget() self.table.setColumnCount(6) self.table.setHorizontalHeaderLabels([ 'TeacherId', 'TeacherFirstName', 'TeacherLastName', 'TeacherGender', 'TeacherTelNo', 'TeacherSchoolId' ])
self.search_input = QLineEdit() self.search_button = QPushButton("Search") self.add_button = QPushButton("Add") self.edit_button = QPushButton("Edit") self.delete_button = QPushButton("Delete") self.prev_button = QPushButton("Prev") self.next_button = QPushButton("Next")
self.pagination_label = QLabel()
search_layout = QHBoxLayout() search_layout.addWidget(self.search_input) search_layout.addWidget(self.search_button)
button_layout = QHBoxLayout() button_layout.addWidget(self.add_button) button_layout.addWidget(self.edit_button) button_layout.addWidget(self.delete_button) button_layout.addWidget(self.prev_button) button_layout.addWidget(self.next_button) button_layout.addWidget(self.pagination_label)
layout = QVBoxLayout() layout.addLayout(search_layout) layout.addWidget(self.table) layout.addLayout(button_layout)
self.main_widget.setLayout(layout) self.sub_window.setWidget(self.main_widget) self.mdi_area.addSubWindow(self.sub_window)
main_layout = QVBoxLayout() main_layout.addWidget(self.mdi_area) self.setLayout(main_layout)
def set_table_data(self, teachers): """ :param teachers: :return: """ self.table.setRowCount(len(teachers)) for row, teacher in enumerate(teachers): self.table.setItem(row, 0, QTableWidgetItem(teacher.teacher_id)) self.table.setItem(row, 1, QTableWidgetItem(teacher.first_name)) self.table.setItem(row, 2, QTableWidgetItem(teacher.last_name)) self.table.setItem(row, 3, QTableWidgetItem(teacher.gender)) self.table.setItem(row, 4, QTableWidgetItem(teacher.tel_no)) self.table.setItem(row, 5, QTableWidgetItem(teacher.school_id))
def get_selected_row(self): """ :return: """ selected_items = self.table.selectedItems() if selected_items: return selected_items[0].row() return -1
def get_search_query(self): """ :return: """ return self.search_input.text()
def show_add_dialog(self, school_map): """ :param school_map: :return: """ dialog = QDialog(self) dialog.setWindowTitle("Add Teacher") layout = QFormLayout()
id_input = QLineEdit() first_name_input = QLineEdit() last_name_input = QLineEdit() gender_input = QLineEdit() tel_input = QLineEdit() school_combo = QComboBox()
# Get all school information schools = school_map # self.school_service.get_schoolall() print("Getting all school information") print(schools) for school in school_map: school_combo.addItem(f"{school.school_id} - {school.school_name}", school.school_id)
layout.addRow("Teacher ID:", id_input) layout.addRow("First Name:", first_name_input) layout.addRow("Last Name:", last_name_input) layout.addRow("Gender:", gender_input) layout.addRow("Tel No:", tel_input) layout.addRow("School:", school_combo)
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) layout.addWidget(button_box)
dialog.setLayout(layout)
if dialog.exec() == QDialog.DialogCode.Accepted: teacher_id = id_input.text() first_name = first_name_input.text() last_name = last_name_input.text() gender = gender_input.text() tel_no = tel_input.text() school_id = school_combo.currentData()
return Teacher(TeacherId=teacher_id, TeacherFirstName=first_name, TeacherLastName=last_name, TeacherGender=gender, TeacherTelNo=tel_no, TeacherSchoolId=school_id) return None
def show_edit_dialog(self, teacher, school_map): """ :param teacher: :param school_map: :return: """ dialog = QDialog(self) dialog.setWindowTitle("Edit Teacher") layout = QFormLayout()
id_input = QLineEdit(teacher.teacher_id) first_name_input = QLineEdit(teacher.first_name) last_name_input = QLineEdit(teacher.last_name) gender_input = QLineEdit(teacher.gender) tel_input = QLineEdit(teacher.tel_no) school_combo = QComboBox()
# Get all school information schools = school_map # self.school_service.get_schoolall() for school in schools: school_combo.addItem(f"{school.school_id} - {school.school_name}", school.school_id) if school.school_id == teacher.school_id: school_combo.setCurrentIndex(school_combo.count() - 1)
layout.addRow("Teacher ID:", id_input) layout.addRow("First Name:", first_name_input) layout.addRow("Last Name:", last_name_input) layout.addRow("Gender:", gender_input) layout.addRow("Tel No:", tel_input) layout.addRow("School:", school_combo)
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) button_box.accepted.connect(dialog.accept) button_box.rejected.connect(dialog.reject) layout.addWidget(button_box)
dialog.setLayout(layout)
if dialog.exec() == QDialog.DialogCode.Accepted: teacher.TeacherId = id_input.text() teacher.TeacherFirstName = first_name_input.text() teacher.TeacherLastName = last_name_input.text() teacher.TeacherGender = gender_input.text() teacher.TeacherTelNo = tel_input.text() teacher.TeacherSchoolId = school_combo.currentData() return teacher return None
def show_message(self, message): """ :param message: :return: """ QMessageBox.information(self, 'Message', message)
def set_pagination_info(self, current_page, total_pages, total_records): """ :param current_page: :param total_pages: :param total_records: :return: """ info_text = f"Current page: {current_page} / Total pages: {total_pages} Total records: {total_records}" self.pagination_label.setText(info_text)
Output:Data processing layers can be handled as mentioned in the previous article. If it involves parallel or multithreading, add the code on top of this architecture.