python: DDD+MVC
项目结构:

# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 22:28
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : school.py
# explain : 学习
class SchoolInfo(object):
"""
领域层(Domain)
学校表
"""
def __init__(self):
"""
構造
"""
self._SchoolId = None
"""
ID,主键
"""
self._SchoolName = None
"""
校名
"""
self._SchoolTelNo = None
"""
学校电话
"""
@property
def SchoolId(self):
"""
ID,主键
"""
return self._SchoolId
@SchoolId.setter
def SchoolId(self, schoolId):
"""
ID,主键
:param SchoolId:
:return:
"""
self._SchoolId = schoolId
@property
def SchoolName(self):
"""
校名
"""
return self._SchoolName
@SchoolName.setter
def SchoolName(self, schoolName):
"""
校名
:param SchoolName:
:return:
"""
self._SchoolName = schoolName
@property
def SchoolTelNo(self):
"""
学校电话
"""
return self._SchoolTelNo
@SchoolTelNo.setter
def SchoolTelNo(self, schoolTelNo):
"""
学校电话
:param SchoolTelNo:
:return:
"""
self._SchoolTelNo = schoolTelNo
# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 22:30
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : school.py
# explain : 学习
import sqlite3
from Domain.school import SchoolInfo
#from __future__ import annotations
from abc import ABC, abstractmethod
from Factory.AbstractFactory import AbstractFactory
class SchoolRepository(object):
"""
基础设施层(Infrastructure)
"""
dal = AbstractFactory.createSchool
def __init__(self):
self.__name = "SchoolBll"
self.createtable() # 如果不存在,初始化表
def __del__(self):
"""
:return:
"""
print(f"{self.__name} ERASE MEMORY")
def Close(cls):
"""
关闭
:return:
"""
cls.dal().Close()
def createtable(self):
"""
建表
"""
self.dal().createtable()
def selectData(self) -> list:
"""
:return:
"""
data = self.dal().selectSql()
return data
def select(self) -> list[SchoolInfo]:
"""
:return:
"""
schools = []
data = self.dal().selectSql()
if len(data) > 0:
for SchoolId, SchoolName, SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectSql(cls) -> list[SchoolInfo]:
"""
元组数据
:return: list 列表
"""
schools = []
data = cls.dal().selectSql()
if len(data) > 0:
for SchoolId, SchoolName, SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectSqlCount(cls) -> int:
"""
查询数据 总数
:return:
"""
# print(cls.dal().selectSqlCount()[0][0])
total = cls.dal().selectSqlCount()[0][0]
return total
def Count(self) -> int:
"""
查询数据 总数
:return:
"""
total = self.dal().selectSqlCount()[0][0]
return total
def getcount(cls, search_query=""):
"""
计算
:param search_query:
:return:
"""
return cls.dal().getcount(search_query)
def getschools(cls, page, limit, search_query=""):
"""
查询 分页
:param page:
:param limit:
:param search_query:
:return:
"""
data = cls.dal().getschools(page, limit, search_query)
print("data:", data)
return data
def selectSqlOrder(cls, order: str) -> list[SchoolInfo]:
"""
元组数据
:param order: SchoolName desc/asc
:return:
"""
schools = []
data = cls.dal().selectSqlOrder(order)
if len(data) > 0:
for SchoolId, SchoolName, SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectSort(cls, field: str, isOrder: bool) -> list[SchoolInfo]:
"""
:param field SchoolId
:param order: desc/asc
:return:
"""
schools = []
data = cls.dal().selectSort(field, isOrder)
if len(data) > 0:
for SchoolId, SchoolName, SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def selectIdSql(cls, SchoolId: str) -> list[SchoolInfo]:
"""
:param SchoolId:ID
:return:
"""
schools = []
data = cls.dal().selectIdSql(SchoolId)
# print(data)
if len(data) > 0:
for SchoolId, SchoolName, SchoolTelNo in data[0]:
info = SchoolInfo()
info.SchoolId = SchoolId
info.SchoolName = SchoolName
info.SchoolTelNo = SchoolTelNo
schools.append(info)
return schools
def addSql(cls, info: SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
return cls.dal().addSql(info)
def add(self, info: SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
return self.dal().addSql(info)
def editSql(cls, info: SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
# print(info)
return cls.dal().editSql(info)
def edit(self, info: SchoolInfo) -> int:
"""
:param info:实体类
:return:
"""
# print(info)
return self.dal().editSql(info)
def delSql(cls, SchoolId: str) -> int:
"""
:param SchoolId:
:return:
"""
return cls.dal().delSql(SchoolId)
def delinfo(self, SchoolId: str) -> int:
"""
:param SchoolId:
:return:
"""
return self.dal().delSql(SchoolId)
# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 22:41
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : SchoolService.py
# explain : 学习
from Domain.school import SchoolInfo
from Infrastructure.school import SchoolRepository
class SchoolService(object):
"""
应用层(Application)
"""
def __init__(self, repository:SchoolRepository):
"""
:param repository:
"""
self.repository = repository
def addschool(self,info: SchoolInfo):
"""
添加学校
:param info:
:return:
"""
self.repository.addSql(info)
def deleteschool(self, school_id):
"""
删除学校
:param school_id:
:return:
"""
self.repository.delSql(school_id)
def updateschool(self, info: SchoolInfo):
"""
更新学校
:param info:
:return:
"""
self.repository.editSql(info)
def getschool(self, school_id)-> list[SchoolInfo]:
"""
查询单个学校
:param school_id:
:return:
"""
return self.repository.selectIdSql(school_id)
def getallschools(self)->[SchoolInfo]:
"""
查询所有学校
:return:
"""
return self.repository.selectSql()
def get_all_schools(self, limit, offset, search_query="") -> list[SchoolInfo]:
"""
分页查询所有学校
:param limit:
:param offset:
:param search_query:
:return:
"""
return self.repository.getschools(limit, offset,search_query)
def get_total_schools(self, search_query=""):
"""
取学校总数
:param search_query:
:return:
"""
return self.repository.getcount(search_query)
# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 22:50
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : SchoolView.py
# explain : 学习
import tkinter as tk
from tkinter import ttk, messagebox
from Presentation.View.SchoolDialog import SchoolDialog
from Presentation.Controller import SchoolController
from Domain.school import SchoolInfo
class SchoolView(tk.Frame):
"""
视图层(View)
"""
def __init__(self, root, controller: SchoolController):
self.controller = controller
self.root = root
self.root.title("学校管理系统")
# 分页大小
self.page_size = 10 # 每页显示 10 条数据
self.current_page = 0 # 当前页码(从 0 开始)
self.search_query = "" # 搜索查询参数
self.limit = 10
# 搜索框和搜索按钮
self.search_entry = tk.Entry(root, width=20)
self.search_entry.grid(row=0, column=0, padx=5, pady=5)
self.search_button = tk.Button(root, text="搜索", command=self.search_schools)
self.search_button.grid(row=0, column=1, padx=5, pady=5)
# 学校列表
self.tree = ttk.Treeview(root, columns=("ID", "名称", "电话"), show="headings")
self.tree.heading("ID", text="学校 ID")
self.tree.heading("名称", text="学校名称")
self.tree.heading("电话", text="学校电话")
self.tree.grid(row=1, column=0, columnspan=4, padx=5, pady=5)
# 绑定双击事件
self.tree.bind("<Double-1>", self.on_tree_double_click)
# 分页控件
self.prev_button = tk.Button(root, text="上一页", command=self.prev_page)
self.prev_button.grid(row=2, column=0, padx=5, pady=5)
self.page_label = tk.Label(root, text="第 1 页")
self.page_label.grid(row=2, column=1, padx=5, pady=5)
self.pagetotal_label = tk.Label(root, text="/共 1 页")
self.pagetotal_label.grid(row=2, column=2, padx=15, pady=15)
self.next_button = tk.Button(root, text="下一页", command=self.next_page)
self.next_button.grid(row=2, column=3, padx=5, pady=5)
# 添加学校按钮
self.add_button = tk.Button(root, text="添加学校", command=self.open_add_dialog)
self.add_button.grid(row=3, column=0, padx=5, pady=5)
# 更新学校按钮
self.update_button = tk.Button(root, text="更新学校", command=self.open_update_dialog)
self.update_button.grid(row=3, column=1, padx=5, pady=5)
# 删除学校按钮
self.delete_button = tk.Button(root, text="删除学校", command=self.delete_school)
self.delete_button.grid(row=3, column=2, padx=5, pady=5)
# 初始化学校列表
self.update_school_list()
def search_schools(self):
"""搜索学校"""
self.search_query = self.search_entry.get()
self.current_page = 0 # 搜索时回到第一页
self.update_school_list()
def on_tree_double_click(self, event):
"""
处理 Treeview 双击事件
:param event:
:return:
"""
selected_item = self.tree.selection()
school=SchoolInfo()
if selected_item:
val=self.tree.item(selected_item)['values']
print("val:",val)
school_id = self.tree.item(selected_item)['values'][0]
school.SchoolId =val[0]
school.SchoolName=val[1]
school.SchoolTelNo=val[2]
dialog = SchoolDialog(self.root, "更新学校","Edit", self.controller, school)
if dialog.result:
school = SchoolInfo()
school.SchoolId = dialog.result[0]
school.SchoolName = dialog.result[1]
school.SchoolTelNo = dialog.result[2]
self.controller.update_school(school)
self.update_school_list()
def open_add_dialog(self):
"""
打开添加学校窗口
"""
dialog = SchoolDialog(self.root, "添加学校","Add", self.controller)
if dialog.result:
print(dialog.result)
school=SchoolInfo()
school.SchoolId=dialog.result[0]
school.SchoolName=dialog.result[1]
school.SchoolTelNo=dialog.result[2]
self.controller.add_school(school)
self.update_school_list()
def open_update_dialog(self):
"""
打开更新学校窗口
"""
selected_item = self.tree.selection()
if selected_item:
school_id = self.tree.item(selected_item, "values")[0]
print("school_id",school_id)
school = self.controller.get_school(school_id)[0]
dialog = SchoolDialog(self.root, "更新学校", "Edit",self.controller, school)
print("dialog",dialog.result[1])
if dialog.result:
school = SchoolInfo()
school.SchoolId = dialog.result[0]
school.SchoolName = dialog.result[1]
school.SchoolTelNo = dialog.result[2]
print("update",school.SchoolName)
self.controller.update_school(school)
self.update_school_list()
else:
messagebox.showwarning("警告", "请选择一个学校!")
def delete_school(self):
"""
删除学校
"""
selected_item = self.tree.selection()
if selected_item:
school_id = self.tree.item(selected_item, "values")[0]
self.controller.delete_school(school_id)
self.update_school_list()
else:
messagebox.showwarning("警告", "请选择一个学校!")
def update_school_list(self):
"""
更新学校列表
"""
self.tree.delete(*self.tree.get_children()) # 清空列表
# 计算分页参数
total_schools = self.controller.get_total_schools(self.search_query)[0][0]
offset=self.current_page+1
schools = self.controller.get_all_schools(offset, self.limit, self.search_query)
# 将数据插入 Treeview
for school in schools:
self.tree.insert("", "end", values=(school))
# 更新分页标签
self.update_page_label()
self.update_page_total()
def prev_page(self):
"""
上一页
"""
if self.current_page >0:
self.current_page -= 1
self.update_school_list()
def next_page(self):
"""
下一页
"""
total_schools = self.controller.get_total_schools(self.search_query)[0][0]
total_pages = (total_schools + self.page_size - 1) // self.page_size
if self.current_page < total_pages - 1:
self.current_page += 1
self.update_school_list()
def update_page_label(self):
"""
更新页码标签
"""
total_schools =self.controller.get_total_schools(self.search_query)[0][0]
total_pages = (total_schools + self.limit - 1) // self.limit
current_page = self.current_page + 1 # 从 1 开始显示
self.page_label.config(text=f"第 {current_page} 页 / 共{total_pages}页")
def update_page_total(self):
"""
更新总页数标签
"""
total_schools = self.controller.get_total_schools(self.search_query)[0][0]
total_pages = (total_schools + self.limit - 1) // self.limit
#print(total_pages)
self.pagetotal_label.config(text=f" 共 {total_schools} /条")
# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 23:51
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : SchoolDialog.py
# explain : 学习
import tkinter as tk
from tkinter import messagebox
from Presentation.Controller import SchoolController
from Domain.school import SchoolInfo
class SchoolDialog:
"""
要区别,添加,修改的操作
"""
def __init__(self, parent, title,action, controller:SchoolController, school=None):
"""
:param parent:
:param title:
:param action:
:param controller:
:param school:
"""
self.controller = controller
self.school = school
self.result = None
self.action= action
self.dialog = tk.Toplevel(parent)
self.dialog.title(title)
# 学校 ID 输入框
self.school_id_label = tk.Label(self.dialog, text="学校 ID")
self.school_id_label.grid(row=0, column=0, padx=5, pady=5)
self.school_id_entry = tk.Entry(self.dialog, width=20)
self.school_id_entry.grid(row=0, column=1, padx=5, pady=5)
# 学校名称输入框
self.school_name_label = tk.Label(self.dialog, text="学校名称")
self.school_name_label.grid(row=1, column=0, padx=5, pady=5)
self.school_name_entry = tk.Entry(self.dialog, width=20)
self.school_name_entry.grid(row=1, column=1, padx=5, pady=5)
# 学校电话输入框
self.school_tel_no_label = tk.Label(self.dialog, text="学校电话")
self.school_tel_no_label.grid(row=2, column=0, padx=5, pady=5)
self.school_tel_no_entry = tk.Entry(self.dialog, width=20)
self.school_tel_no_entry.grid(row=2, column=1, padx=5, pady=5)
# 确定按钮
self.ok_button = tk.Button(self.dialog, text="确定", command=self.on_ok)
self.ok_button.grid(row=3, column=0, padx=5, pady=5)
# 取消按钮
self.cancel_button = tk.Button(self.dialog, text="取消", command=self.dialog.destroy)
self.cancel_button.grid(row=3, column=1, padx=5, pady=5)
print(self.school)
if self.action == "Edit":
# 如果是更新操作,填充现有数据
if self.school:
self.school_id_entry.insert(0, self.school.SchoolId)
self.school_name_entry.insert(0, self.school.SchoolName)
self.school_tel_no_entry.insert(0, self.school.SchoolTelNo)
self.school_id_entry.config(state="disabled") # 禁止修改 ID
# 等待窗口关闭
self.dialog.wait_window()
def on_ok(self):
"""
确定按钮点击事件
"""
school_id = self.school_id_entry.get()
name = self.school_name_entry.get()
tel_no = self.school_tel_no_entry.get()
if school_id and name and tel_no:
print("name",name)
self.result = (school_id, name, tel_no)
''' 也可以修改
if self.action=="Edit":
school = SchoolInfo()
school.SchoolId = self.result[0]
school.SchoolName = self.result[1]
school.SchoolTelNo = self.result[2]
print("update", school.SchoolName)
self.controller.update_school(school)
'''
self.dialog.destroy()
else:
messagebox.showwarning("警告", "所有字段不能为空!")
# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 22:48
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : SchoolController.py
# explain : 学习
from Application.SchoolService import SchoolService
from Domain.school import SchoolInfo
class SchoolController(object):
"""
控制器层(Controller)
"""
def __init__(self, service:SchoolService):
"""
:param service:
"""
self.service = service
def add_school(self, info:SchoolInfo):
"""
:param info:
:return:
"""
self.service.addschool(info)
def delete_school(self, school_id):
"""
:param school_id:
:return:
"""
self.service.deleteschool(school_id)
def update_school(self, info:SchoolInfo):
"""
:param info:
:return:
"""
self.service.updateschool(info)
def get_school(self, school_id)-> list[SchoolInfo]:
"""
:param school_id:
:return:
"""
return self.service.getschool(school_id)
def get_all_schools(self, limit, offset=0, search_query=""):
"""
:param limit:
:param offset:
:param search_query:
:return:
"""
return self.service.get_all_schools(limit, offset,search_query)
def get_total_schools(self, search_query=""):
"""
:param search_query:
:return:
"""
return self.service.get_total_schools(search_query)
主程序:
# encoding: utf-8
# 版权所有 2025 涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# Author : geovindu,Geovin Du 涂聚文.
# IDE : PyCharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgreSQL 17.0 Oracle 21c
# Datetime : 2025/2/14 22:28
# User : geovindu
# Product : PyCharm
# Project : pydddmvcdemo
# File : main.py
# explain : 学习
import tkinter as tk
from Infrastructure.school import SchoolRepository
from Application.SchoolService import SchoolService
from Presentation.Controller.SchoolController import SchoolController
from Presentation.View.SchoolView import SchoolView
if __name__ == '__main__':
# 创建根窗口
root = tk.Tk()
# 初始化 DDD 分层组件
repository = SchoolRepository()
service = SchoolService(repository)
controller = SchoolController(service)
# 初始化视图
view = SchoolView(root, controller)
root.iconbitmap('favicon.ico')
# 启动主循环
root.mainloop()
print('PyCharm,geovindu')
输出:

哲学管理(学)人生, 文学艺术生活, 自动(计算机学)物理(学)工作, 生物(学)化学逆境, 历史(学)测绘(学)时间, 经济(学)数学金钱(理财), 心理(学)医学情绪, 诗词美容情感, 美学建筑(学)家园, 解构建构(分析)整合学习, 智商情商(IQ、EQ)运筹(学)生存.---Geovin Du(涂聚文)
浙公网安备 33010602011771号