python tornado 实现类禅道系统
最近楼主加班 喽, 好久没有更新我的博客了,哎,一言难尽,废话我就不说了,来开始上精华。
背景:目前市面上有很多bug管理工具,但是各有各的特点,最著名,最流行的就是禅道,一个偶然的机会接触到了python ,学到tornado后,就想着去怎么去用到实处,后来发现自己公司的除了禅道就记录bug没有什么可以用的工具了。
语言:python3 第三库 :tornado,qiniu(用于云存储文件),数据库用sqlite
why use tornado?很多人其实会这么问我,我感觉tornado可以实现异步,虽然现在代码还没有用到异步,我感觉还是很不错的框架,值得学习,现在很多公司都在用,个人感觉这是一个不错的,值得我们大家去学习的框架。
来看看我的需求文档
大题是这么的轮廓,那么拿到这个的时候,我会进行需求分析,
需要什么样的数据库, 数据模型之间的关系,虽然现在还是有很多地方是写死的还没有进行搜索功能的设置,但是我相信,有了现在这个demo
那么我开始来设计下我主要会结构
handlsers 存放类似flask的views
models存数据库相关的,
static存放静态
template存放模板
untils存放公共库
setting 配置文件
urls url地址映射
run 运行文件。
那么我来开始设计我的数据,其实我的数据模型也是一波三折的。
选择用了sqlalchemy,之前用过flask的sqlalchemy感觉不错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | from models.dataconfig import db_session,Base,create_all from sqlalchemy import Column,Integer,DateTime,Boolean,String,ForeignKey,desc,asc,Text from sqlalchemy.orm import relationship,backref from untils.common import encrypt import datetime class User(Base): __tablename__ = 'users' id = Column(Integer(),primary_key = True ) username = Column(String( 64 ),unique = True ,index = True ) email = Column(String( 64 )) password = Column(String( 64 )) last_logtime = Column(DateTime()) status = Column(Integer()) leves = Column(Integer()) iphone = Column(Integer()) Projects = relationship( 'Project' ,backref = 'users' ) shebei = relationship( 'Shebei' ,backref = 'users' ) file = relationship( 'FilePan' ,backref = 'users' ) banben = relationship( 'BanbenWrite' ,backref = 'users' ) testresult = relationship( 'TestResult' ,backref = 'users' ) testcase = relationship( 'TestCase' ,backref = 'users' ) buglog = relationship( 'BugLog' ,backref = 'users' ) def __repr__( self ): return self .username @classmethod def get_by_id( cls , id ): item = db_session.query(User). filter (User. id = = id ).first() return item @classmethod def get_by_username( cls , username): item = db_session.query(User). filter (User.username = = username).first() return item @classmethod def get_count( cls ): return db_session.query(Shebei).count() @classmethod def add_new( cls ,username,password,iphone,email,leves): new = User(username = username,iphone = iphone,email = email,leves = leves) new.password = encrypt(password) new.status = 0 db_session.add(new) try : db_session.commit() except : db_session.rollback() class Shebei(Base): __tablename__ = 'shebeis' id = Column(Integer(),primary_key = True ) shebei_id = Column(String( 32 ),unique = True ) shebei_name = Column(String( 64 )) shebei_xitong = Column(String( 64 )) shebei_xinghao = Column(String( 255 )) shebei_jiage = Column(Integer()) shebei_fapiaobianhao = Column(String( 64 )) shebei_quanxian = Column(Boolean()) shebei_jie = Column(Boolean()) shebei_shuyu = Column(String()) shebei_date = Column(DateTime()) shebei_user = Column(String()) gou_date = Column(DateTime()) shebei_status = Column(String( 16 )) she_sta = Column(Integer(),default = 0 ) ruku_user = Column(Integer(),ForeignKey( 'users.id' )) def __repr__( self ): return self .shebei_name @classmethod def get_by_name( cls ,name): item = db_session.query(Shebei). filter (Shebei.shebei_name = = name).first() return item @classmethod def get_by_id( cls , id ): item = db_session.query(Shebei). filter (Shebei. id = = id ).first() return item @classmethod def get_count( cls ): return db_session.query(Shebei).count() class TestResult(Base): __tablename__ = 'testresults' id = Column(Integer(),primary_key = True ) porject_id = Column(Integer(),ForeignKey( 'projects.id' )) creat_time = Column(DateTime()) bug_first = Column(Integer()) ceshirenyuan = Column(String( 255 )) is_send = Column(Boolean(),default = True ) filepath = Column(String( 64 )) status = Column(Integer(),default = 0 ) user_id = Column(Integer(),ForeignKey( 'users.id' )) def __repr__( self ): return self .porject_name @classmethod def get_by_name( cls ,name): item = db_session.query(TestResult). filter (TestResult.porject_name = = name).first() return item @classmethod def get_by_id( cls , id ): item = db_session.query(TestResult). filter (TestResult. id = = id ).first() return item @classmethod def get_by_user_id( cls ,user_id): item = db_session.query(TestResult). filter (TestResult.user_id = = user_id).first() return item @classmethod def get_count( cls ): return db_session.query(TestResult).count() class BanbenWrite(Base): __tablename__ = 'banbens' id = Column(Integer(),primary_key = True ) porject_id = Column(Integer(),ForeignKey( 'projects.id' )) creat_time = Column(DateTime(),default = datetime.datetime.now()) banbenhao = Column(String( 32 )) is_xian = Column(Boolean(),default = False ) is_test = Column(Boolean(),default = False ) status = Column(Integer()) user_id = Column(Integer(),ForeignKey( 'users.id' )) bugadmin = relationship( 'BugAdmin' ,backref = 'banbens' ) def __repr__( self ): return self .banbenhao @classmethod def get_by_name( cls ,name): item = db_session.query(BanbenWrite). filter (BanbenWrite.porject_name = = name).first() return item @classmethod def get_by_id( cls , id ): item = db_session.query(BanbenWrite). filter (BanbenWrite. id = = id ).first() return item @classmethod def get_by_user_id( cls ,user_id): item = db_session.query(BanbenWrite). filter (BanbenWrite.user_id = = user_id).first() return item @classmethod def get_count( cls ): return db_session.query(BanbenWrite).count() class FilePan(Base): __tablename__ = 'files' id = Column(Integer(),primary_key = True ) file_fenlei = Column(String( 64 )) file_name = Column(String( 64 )) down_count = Column(Integer(),default = 0 ) creat_time = Column(DateTime(),default = datetime.datetime.now()) status = Column(Integer(),default = 0 ) down_url = Column(String( 64 )) is_tui = Column(Boolean(),default = False ) user_id = Column(Integer(),ForeignKey( 'users.id' )) def __repr__( self ): return self .file_name @classmethod def get_by_file_name( cls ,name): item = db_session.query(FilePan). filter (FilePan.file_name = = name).first() return item @classmethod def get_by_id( cls , id ): item = db_session.query(FilePan). filter (FilePan. id = = id ).first() return item @classmethod def get_by_user_id( cls ,user_id): item = db_session.query(FilePan). filter (FilePan.user_id = = user_id).first() return item @classmethod def get_count( cls ): return db_session.query(FilePan).count() class BugAdmin(Base): __tablename__ = 'bugadmins' id = Column(Integer(),primary_key = True ) porject_id = Column(Integer(),ForeignKey( 'projects.id' )) bugname = Column(String( 64 )) bugdengji = Column(String( 64 )) bugtime = Column(DateTime(),default = datetime.datetime.now()) bug_miaoshu = Column(String( 255 )) ban_id = Column(Integer(),ForeignKey( 'banbens.id' )) fujian = Column(String( 64 )) is_que = Column(Boolean()) bug_status = Column(String( 64 )) bug_jiejuefangan = Column(String( 64 )) bug_send = Column(String( 64 )) status = Column(Integer(),default = 0 ) bug_log = relationship( 'BugLog' ,backref = 'bugadmins' ) user_id = Column(Integer(),ForeignKey( 'users.id' )) def __repr__( self ): return self .bugname @classmethod def get_by_bugname( cls ,bugname): item = db_session.query(BugAdmin). filter (BugAdmin.bugname = = bugname).first() return item @classmethod def get_by_id( cls , id ): item = db_session.query(BugAdmin). filter (BugAdmin. id = = id ).first() return item @classmethod def get_by_porject_name( cls ,porject_name): item = db_session.query(BugAdmin). filter (BugAdmin.porject_name = = porject_name).first() return item @classmethod def get_count( cls ): return db_session.query(BugAdmin).count() class TestCase(Base): __tablename__ = 'testcases' id = Column(Integer(),primary_key = True ) porject_id = Column(Integer(),ForeignKey( 'projects.id' )) casename = Column(String( 64 )) case_qianzhi = Column(String()) case_buzhou = Column(String()) case_yuqi = Column(String()) status = Column(Integer(),default = 0 ) case_crea_time = Column(DateTime(),default = datetime.datetime.now()) user_id = Column(Integer(),ForeignKey( 'users.id' )) def __repr__( self ): return self .casename @classmethod def get_by_project_name(Cls,project_name): item = db_session.query(TestCase). filter (TestCase.project_name = = project_name).first() return item @classmethod def get_by_casename(Cls,casename): item = db_session.query(TestCase). filter (TestCase.casename = = casename).first() return item @classmethod def get_by_id( cls , id ): item = db_session.query(TestCase). filter (TestCase. id = = id ).first() return item @classmethod def get_count( cls ): return db_session.query(TestCase).count() class BugLog(Base): __tablename__ = 'buglogs' id = Column(Integer(),primary_key = True ) bug_id = Column(Integer(),ForeignKey( 'bugadmins.id' )) caozuo = Column(String()) caozuo_time = Column(DateTime()) user_id = Column(Integer(),ForeignKey( 'users.id' )) def __repr__( self ): return self .caozuo @classmethod def get_by_id(Cls, id ): item = db_session.query(BugLog). filter (BugLog. id = = id ).first() return item @classmethod def get_by_user_id(Cls,user_id): item = db_session.query(BugLog). filter (BugLog.user_id = = user_id).first() return item @classmethod def get_by_bug_id(Cls,bug_id): item = db_session.query(BugLog). filter (BugLog.bug_id = = bug_id).first() return item class Project(Base): __tablename__ = 'projects' id = Column(Integer(),primary_key = True ) name = Column(String( 64 )) user_id = Column(Integer(),ForeignKey( 'users.id' )) bug_log = relationship( 'BugAdmin' ,backref = 'projects' ) banben = relationship( 'BanbenWrite' ,backref = 'projects' ) testresult = relationship( 'TestResult' ,backref = 'projects' ) testcase = relationship( 'TestCase' ,backref = 'projects' ) def __repr__( self ): return self .name @classmethod def get_by_id( cls , id ): item = db_session.query(Project). filter (Project. id = = id ).first() return item @classmethod def get_by_name( cls ,name): item = db_session.query(Project). filter (Project.name = = name).first() return item |
这是数据库相关的,
1 数据库配置相关的 2 3 from sqlalchemy import create_engine 4 from sqlalchemy.orm import scoped_session,sessionmaker 5 from sqlalchemy.ext.declarative import declarative_base 6 engine=create_engine('sqlite:///shebei.db',convert_unicode=True) 7 Base=declarative_base() 8 db_session=scoped_session(sessionmaker(bind=engine)) 9 def create_all(): 10 Base.metadata.create_all(engine) 11 def drop_all(): 12 Base.metadata.drop_all(engine)
其实在开发的过程中,也遇到了很多阻力,比如下载一直实现不好,比如分页,也是参照别人的实现的,
分页公共模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class Pagination: def __init__( self , current_page, all_item): try : page = int (current_page) except : page = 1 if page < 1 : page = 1 all_pager, c = divmod (all_item, 10 ) if int (c) > 0 : all_pager + = 1 self .current_page = page self .all_pager = all_pager @property def start( self ): return ( self .current_page - 1 ) * 10 @property def end( self ): return self .current_page * 10 def string_pager( self , base_url = "/index/" ): if self .current_page = = 1 : prev = '<li><a href="javascript:void(0);">上一页</a></li>' else : prev = '<li><a href="%s%s">上一页</a></li>' % (base_url, self .current_page - 1 ,) if self .current_page = = self .all_pager: nex = '<li><a href="javascript:void(0);">下一页</a></li>' else : nex = '<li><a href="%s%s">下一页</a></li>' % (base_url, self .current_page + 1 ,) last = '<li><a href="%s%s">尾页</a></li>' % (base_url, self .all_pager,) str_page = "".join((prev,nex,last)) return str_page |
在上传文件的时候,原来存放在本地,结果呢,下载处理不好,于是乎选择了七牛,需要到七牛的官网去注册自己的账号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from qiniu import Auth,put_file,etag,urlsafe_base64_encode import qiniu.config access_key = 'uVxowDUcYx641ivtUb111WBEI4112L3D117JHNM_AOtskRh4' secret_key = 'PdXU9XrXTLtp1N21bhU1Frm1FDZqE1qhjkEaE9d1xVLZ5C' def sendfile(key, file ): q = Auth(access_key,secret_key) bucket_name = 'leilei22' token = q.upload_token(bucket_name, key) ret, info = put_file(token, key, file ) me = ret[ 'hash' ] f = etag( file ) if me = = f: assert_t = True else : assert_t = False return assert_t |
解析Excel,主要用于上传测试用例
import xlrd,xlwt from xlutils.copy import copy def datacel(filepath): file=xlrd.open_workbook(filepath) me=file.sheets()[0] nrows=me.nrows porject_id_list=[] casename_list=[] case_qianzhi_list=[] case_buzhou_list=[] case_yuqi_list=[] for i in range(1,nrows): porject_id_list.append(me.cell(i,0).value) casename_list.append(me.cell(i,2).value) case_qianzhi_list.append(me.cell(i,3).value) case_buzhou_list.append(me.cell(i,4).value) case_yuqi_list.append(me.cell(i,1).value) return porject_id_list,casename_list,case_qianzhi_list,case_buzhou_list,case_yuqi_list
其实这么现在公共模块完毕了,其实现在可以着手去开始写我们的代码了,主要的代码,还有静态界面,因为前后端都是我自己,我的前端其实还是从网上找来的模板,
学习的道路是痛苦的,但是我相信我是可以成功的,
1 2 3 4 5 6 7 8 9 10 11 | from tornado.web import RequestHandler from models.model_py import User class BaseHandler(RequestHandler): @property def db( self ): return self .application.db def get_current_user( self ): user_id = self .get_secure_cookie( 'user_id' ) if not user_id: return None return User.get_by_id( int (user_id)) |
基础的类集成了RequestHandler的类,,进行了一些简单的自定义,然后后续可以用这个,
进过两周的开发,已经形成了成熟的,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?