选课系统编写思路
搭建框架
- 依旧是使用三层架构的框架来编写程序
- 编写的时候同样使用软件开发目录规范
- 不同的是尝试将面向对象的三大核心——封装、继承、多态尽量应用进去
编写思路
-
创建开发目录及可能要用到的文件
-
架构设计
-
编写start、src、三个视窗,用空函数体搭建起用户交互层,枚举出功能
点击查看代码
from core import admin, teacher, student modules_dict = { '1': admin, '2': teacher, '3': student } def run(): while True: choice = input(""" ===========中国'慕棵'选课系统========== 1 管理员通道 2 老师通道 3 学生通道 ===============<首页>================ 请输入视图编号(q退出程序):""") if choice == 'q': print('bye~~') return if choice in modules_dict: modules_dict.get(choice).run() else: print('请输入正确的编号')
-
编写管理员的注册功能,串写一遍架构,能帮助我们理清思路,调什么接口,接口调什么类
-
注册时发现类体中,独有的数据属性是实现各功能关键,根据每类对象的功能,列出选课系统中所有类可能用到的数据属性,并编写对应类的双下init功能。
如下,这里的程序中顺带将处理数据的dbhandler写了,相较原本只能读写一种数据的select和save,这次我们采取了子类继承父类的select和save方法,让它们根据类和对象自行判断存储的路径,让不同的数据对象存到了不同的文件夹下以便管理。
还有,日志功能也写到了这里,每个类中都产生了一个记录者,这样当在有对象进行一些功能的操作时,就会访问到类中的记录者,对程序进行记录。
对于封装和伪装也少量的应用了一下,逻辑是:name属性作为唯一凭证,只能查看,不能修改;
密码不能查看,只能校验和修改,所以单独开放了两个功能。
点击查看代码
```py import logging import os import pickle from lib import common from conf import settings class DBHandler: __name = '子类中应该有__name作为唯一凭证' __pwd = '对于有密码的子类可以校验密码和更改密码' def save(self): path_dir = os.path.join(settings.DB_DIR, self.__class__.__name__) if not os.path.isdir(path_dir): os.mkdir(path_dir) obj_path = os.path.join(path_dir, self.name) with open(obj_path, 'wb') as f: pickle.dump(self, f) @classmethod def select(cls, name): path_dir = os.path.join(settings.DB_DIR, cls.__name__) if not os.path.isdir(path_dir): os.mkdir(path_dir) obj_path = os.path.join(path_dir, name) if os.path.isfile(obj_path): with open(obj_path, 'rb') as f: return pickle.load(f) @classmethod def select_all_obj_name(cls): # 拿到类下的所有对象 path_dir = os.path.join(settings.DB_DIR, cls.__name__) if not os.path.isdir(path_dir): os.mkdir(path_dir) return os.listdir(path_dir) # 可能返回一个名称列表或者空列表 class Admin(DBHandler): logger = logging.getLogger('管理员记录') def __init__(self, name, pwd): self.__name = name self.__pwd = pwd self.save() @property def name(self): return self.__name def verify_pwd(self, pwd): return self.__pwd == pwd def alter_pwd(self, new_pwd): self.__pwd = new_pwd class School(DBHandler): logger = logging.getLogger('学校记录') def __init__(self, name, addr): self.__name = name self.addr = addr self.course_list = [] self.valid = True self.save() @property def name(self): return self.__name class Teacher(DBHandler): logger = logging.getLogger('老师记录') def __init__(self, name, pwd): self.__name = name self.__pwd = pwd self.valid = True self.course_list = [] self.save() @property def name(self): return self.__name def verify_pwd(self, pwd): return self.__pwd == pwd def alter_pwd(self, new_pwd): self.__pwd = new_pwd class Course(DBHandler): logger = logging.getLogger('课程记录') def __init__(self, name, period, price, tea_name, sch_name): self.__name = name self.valid = True self.__period = period self.__price = price self.__major_tea = tea_name self.school = sch_name self.tea_list = [] self.stu_list = [] self.email = {} # {stu:msg} self.save() @property def name(self): return self.__name @property def showed_infos(self): return [self.name, self.__period, self.__major_tea, self.__price, self.school] @property def all_stu_scores(self): return [Student.select(stu).scores_list[self.name] for stu in self.stu_list] class Student(DBHandler): logger = logging.getLogger('学生记录') def __init__(self, name, pwd, age): self.__name = name self.__pwd = pwd self.age = age self.valid = True self.school = '' self.course_list = [] self.scores_list = {} self.account = 0 self.email = {} # {course:msg(哪个老师的处理结果)} self.save() @property def name(self): return self.__name def verify_pwd(self, pwd): return self.__pwd == pwd def alter_pwd(self, new_pwd): self.__pwd = new_pwd
</details>
-
编写每个接口的注册登录功能,因为其类似性,也可以将其整合起来,用传入参数的方式来更改一些字符,使一段代码可以满足所有的注册登录。
而如何通过字符来调用不同的接口,显示不同的字符结果,我用到的是发射和配置字典
点击查看代码
# settings里写的配置字典 MODE_DICT = { 'Admin': {'named': '管理员', }, 'Teacher': {'named': '教师', }, 'Student': {'named': '学生', }, 'Course': {'named': '课程'} }
# 专门写了一个py文件用于存入用户的登录态,和组织用户注册登录的功能 from interface import user_inf from conf import settings from lib import common login_state = { 'Admin': '', 'Teacher': '', 'Student': '', } def register(mode): view_str = settings.MODE_DICT[mode].get('named') while True: print(f'--【{view_str}】注册界面--') username = input('请输入用户名:').strip() if user_inf.is_user_exist(username, mode=mode): print('用户已经被注册过了') continue password = input('请输入密码:').strip() confirm_pwd = input('请确认密码:').strip() if not password == confirm_pwd: print('两次密码不一致') continue password = common.get_hash(password) if mode == 'Admin': user_inf.register_inf(username, password, mode=mode) break if mode == 'Student': while True: # 学生还有些额外的要输入的东西,让用户可以循环输入 age = input('请输入你的年龄:').strip() if not age.isdigit(): print('请输入数字') continue break user_inf.register_inf(username, password, mode=mode, age=age) break print(f'{view_str}【{username}】注册成功') print('~返回登录注册界面~') def login(mode): view_str = settings.MODE_DICT[mode].get('named') while True: print(f'--{view_str}登录功能--') username = input('请输入用户名(q返回上一页):').strip() if username == 'q': return False if not user_inf.is_user_exist(username, mode=mode): print('用户不存在') continue password = input('请输入密码:').strip() password = common.get_hash(password) flag = user_inf.login_inf(username, password, mode=mode) if flag == -1: print(f'用户{username}已失效,请联系管理员') return False if flag: print(f'{view_str}【{username}】登录成功!') login_state[mode] = username return True print('密码不对!') def logout(mode): view_str = settings.MODE_DICT[mode].get('named') name = login_state[mode] login_state[mode] = '' return f'{view_str}【{name}】已登出' def login_auth(mode): def outer(func): def inner(*args, **kwargs): if login_state[mode]: res = func(*args, **kwargs) return res print('您还没有登录,为您跳转到登录界面') login(mode) return inner return outer
# 用于接受注册登录的接口文件user_inf.py from db import models def is_user_exist(username, mode): cls = getattr(models, mode) if cls.select(username): return True return False def register_inf(username, pwd, *, mode, **kwargs): cls = getattr(models, mode) cls(username, pwd, **kwargs) def login_inf(username, pwd, *, mode): cls = getattr(models, mode) user_obj = cls.select(username) if hasattr(user_obj, 'valid'): # 如果有这个属性 if not user_obj.valid: # 失效情况下,返回-1 return -1 return user_obj.verify_pwd(pwd)
-
完成注册登录功能的独立后,尝试将注册登录与各视图的核心功能给分开,如图:
实际上就是采取了两个循环并列,当登录循环从登录成功结束时,进入核心功能循环。
-
实现管理员创建功能,每个创建功能都大同小异,要求用户输入,判断是否存在同名对象,如果无同名则去调创建的接口,录入一些信息,并在管理员的类中加入一个创建xx的功能,做对应的日志记录,调用相应的类,并保存到pickle文件中。
def create_school(): while True: sch_name = input('请输入注册的学校名(q返回上一页):').strip() if sch_name == 'q': return if user_inf.is_user_exist(sch_name, 'School'): print('该学校已经注册过了') continue addr = input('输入学校地址:').strip() admin_inf.create_school_inf(sch_name, addr, user_login.login_state[MODE]) print(f'学校{sch_name}创建成功!')
-
实现学生选学校功能,选课程功能
先判断学生是否绑定过学校,没有就要求绑定学校,有才能选课。
都是先打印有效的学校或者绑定学校下的有效课程,调学生接口用类方法取所有的学校对象,和用对象方法取学校下的课程,拿到相应的信息,这里打印表格调用了common中的功能,使用第三方模块,快速的将二维列表打印成表格的样式。
再要求用户输入相应的编号,如果编号输入有问题就重新输,直到按q退出,有效编号就转化为列表中的对应对象名称,我们再将学校课程的名字传入接口层处理,都调用学生的对象方法使学生和学校、课程互相绑定。
def choose_school(): # 先判断学生有没有绑定过学校 sch_name = student_inf.is_bind_school(user_login.login_state[MODE]) if sch_name: ask = input('你已经绑定过学校了,是否解绑(y/n)').strip() if ask != 'y': print(f'保持学校{sch_name}的绑定,正在返回学生菜单') return print('已解绑原学校,可以重新绑定其他学校') while True: # 打印名单 name_list = public_inf.get_valid_name('School') print(common.get_index_table([f'学校名'], name_list)) choice = input(f'输入学校编号以绑定(q返回学生菜单):').strip() if choice == 'q': return if not choice.isdigit(): print('请输入数字') continue choice = int(choice) - 1 if choice not in range(len(name_list)): print('请输入有效编号') continue sch_name = name_list[choice] # 绑定学校 student_inf.bind_school_inf(sch_name, user_login.login_state[MODE]) print(f'绑定学校{sch_name}成功') break
-
实现学生查分功能,预留有分数独有数据,我们只要在类方法中将这个数据返回出去,接口层处理为易于展示的二维列表,交互层在调用打印表格的公共方法即可打印学生的分数
-
实现老师选课、查课
选课是将所有的课程对象拿过来选,查课是通过自己对象中的course_list的数据属性来查看
-
实现老师查分、改分
老师查看自己的课表,选一门课来查看,这样就能看到课下所有学生这门课的分数。
改分就是在查分的基础上,选择一个学生来修改分数。
这里涉及的数据操作无外乎对老师的对象的课程进行遍历,对课程对象的学生进行遍历,对学生对象的分数属性进行修改。