Implementing DDD with ORM and PyQt6 Using MySQL

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:

Implementing DDD with ORM and PyQt6 Using MySQL

# 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:Implementing DDD with ORM and PyQt6 Using MySQLData 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.Implementing DDD with ORM and PyQt6 Using MySQL

Leave a Comment