在上一篇博客中,我们完成了考勤系统的基本功能。现在,让我们继续开发请假系统。
十一 构建事件种类
还记得我们之前建立的考勤事件么?在当前的设计中,考勤事件有两个字段:事件代码和事件名称,现在我们决定给考勤事件增加一个种类的字段,来表明该事件属于何种事件。
我们需要建立一个新表来存储考勤事件的种类,就叫它TimeSheetEventCategory吧。
在database目录下建立tbltimesheeteventcategory.py,输入以下代码:
# database/tbltimesheeteventcategory.py
from database.tablebase import Base
from sqlalchemy import Column,String,Integer,Date,Boolean
class TimeSheetEventCategory(Base):
__tablename__ = 'timesheeteventcategory'
id = Column(Integer,autoincrement=True,primary_key=True)
# 类别名称
eventcategoryname = Column(String,unique=True,nullable=False)
# 创建时间
createdate = Column(Date,nullable=False)
def __repr__(self):
return '<timesheeteventcategory(eventcategoryname=%s)>' % (self.eventcategoryname)
该表很简单,只有三个字段:内部编号id,类别名称eventcategoryname和创建时间createdate。
进入migrate目录下,在powershell输入以下命令:
alembic revision -m "add timesheeteventcategory"
在新生成的py文件中,编写upgrade函数:
def upgrade():
op.create_table(
'timesheeteventcategory',
sa.Column('id', sa.Integer, primary_key=True, autoincrement=True),
sa.Column('eventcategoryname', sa.String, nullable=False),
sa.Column('createdate', sa.Date, nullable=False),
)
然后运行以下命令,完成建表。
alembic upgrade head
接下来,让我们开始编写创建事件类别的后端部分。打开util/timesheet/timesheetutil.py,实现createtimesheeteventcategory函数:
# util/timesheet/timesheetutil.py
# ...
from database.tbltimesheeteventcategory import TimeSheetEventCategory
def createtimesheeteventcategory(categoryname):
timesheeteventcategory = session.query(TimeSheetEventCategory).filter(TimeSheetEventCategory.eventcategoryname == categoryname)
result = 'Fail'
if type(timesheeteventcategory) is not TimeSheetEventCategory:
newtimesheeteventcategory = TimeSheetEventCategory(eventcategoryname=categoryname,createdate=datetime.date.today())
result = insertdata(newtimesheeteventcategory)
return result
然后,在apps/timesheet_app/timesheet_app.py中实现CreateTimeSheetEventCategory:
# apps/timesheet_app/timesheet_app.py
# ...
from database.tbltimesheeteventcategory import TimeSheetEventCategory
from util.timesheet.timesheetutil import createtimesheeteventcategory
class CreateTimeSheetEventCategory(BaseHandler):
def get(self):
timesheeteventpath = gettemplatepath('createtimesheeteventcategory.html')
timesheeteventcategorys = session.query(TimeSheetEventCategory)
self.render(timesheeteventpath,timesheeteventcategorys=timesheeteventcategorys)
def post(self):
categoryname = self.get_argument('categoryname')
result = createtimesheeteventcategory(categoryname)
resultpath = gettemplatepath('result.html')
if result == 'Fail':
self.render(resultpath,result=result)
else:
self.redirect('/createtimesheeteventcategory')
# server/main.py
# ...
routelist = [
# ...
(r"/createtimesheeteventcategory", CreateTimeSheetEventCategory),
# ...
]
这个页面的上半部分会显示出目前建立的事件类别,而底下会提供一个表单来建立新的事件类别,前端部分代码如下:
<!--createtimesheeteventcategory-->
{% block content %}
<div class="page-wrapper">
<!-- ============================================================== -->
<!-- Container fluid -->
<!-- ============================================================== -->
<div class="container-fluid">
<!-- ============================================================== -->
<!-- Bread crumb and right sidebar toggle -->
<!-- ============================================================== -->
<div class="row page-titles">
<div class="col-md-6 col-8 align-self-center">
<h3 class="text-themecolor m-b-0 m-t-0">创建考勤事件类别</h3>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">创建考勤事件类别</li>
</ol>
</div>
</div>
<!-- ============================================================== -->
<!-- End Bread crumb and right sidebar toggle -->
<!-- ============================================================== -->
<!-- ============================================================== -->
<!-- Start Page Content -->
<!-- ============================================================== -->
<!-- Row -->
<div class="row">
<!-- Column -->
<div class="col-lg-8 col-xlg-9 col-md-7">
<div class="card">
<div class="card-block">
<h4 class="card-title">当前考勤事件类别</h4>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>#</th>
<th>事件类别</th>
<th>创建时间</th>
</tr>
</thead>
<tbody>
{% for category in timesheeteventcategorys %}
<tr>
<td>{{ category.id }}</td>
<td>{{ escape(category.eventcategoryname) }}</td>
<td>{{ category.createdate }}</td>
</tr>
{% end %}
</tbody>
</table>
</div>
</div>
<div class="card-block">
<form class="form-horizontal form-material" action="/createtimesheeteventcategory" method="post" >
<div class="form-group">
<label class="col-md-12">事件类 *</label>
<div class="col-md-12">
<input type="text" class="form-control form-control-line" required=true name="categoryname" id="categoryname"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-success">创建</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Column -->
</div>
<!-- Row -->
<!-- ============================================================== -->
<!-- End PAge Content -->
<!-- ============================================================== -->
</div>
<!-- ============================================================== -->
<!-- End Container fluid -->
<!-- ============================================================== -->
<!-- ============================================================== -->
<!-- footer -->
<!-- ============================================================== -->
<footer class="footer text-center">
© 2020 Tornado考勤系统
</footer>
<!-- ============================================================== -->
<!-- End footer -->
<!-- ============================================================== -->
</div>
{% end %}
效果如下:
从图中可知,我们建立了三个事件类别:Vacation、Work和Weekend,而在底下的表单中我们可以随时增加新的事件类别。
现在,让我们对之前的创建考勤事件功能做一些小改动,让考勤事件与类别相关联,并且也给已经建立好的考勤事件赋予类别。
首先别忘了给TimeSheetEvent添加eventcategory栏位,该栏位为String类型,这里不再赘述。
打开util/timesheet/timesheetutil.py,修改createtimesheetevent函数:
# util/timesheet/timesheetutil.py
# ...
def createtimesheetevent(eventcode,nickname,eventcategory):
timesheetevent = session.query(TimeSheetEvent).filter(TimeSheetEvent.eventcode == eventcode).first()
result = 'Fail'
if type(timesheetevent) is not TimeSheetEvent:
newtimesheetevent = TimeSheetEvent(eventcode=eventcode,nickname=nickname,eventcategory=eventcategory)
result = insertdata(newtimesheetevent)
# Modify by DS Liu 2021/1/23
else:
timesheetevent.eventcategory = eventcategory
result = insertdata(timesheetevent)
return result
这个函数和之前相比,多了eventcategory的参数;并且,当eventcode存在的时候,我们不是直接报错,而是给已有的事件加上eventcategory值。
在timesheet_app.py中的CreateTimeSheetEvent中也加入对应的代码:
# timesheet_app.py
class CreateTimeSheetEvent(BaseHandler):
def get(self):
timesheeteventpath = gettemplatepath('createtimesheetevent.html')
timesheetevents = session.query(TimeSheetEvent)
timesheeteventcategory = session.query(TimeSheetEventCategory)
self.render(timesheeteventpath,timesheetevents=timesheetevents,timesheeteventcategorys=timesheeteventcategory)
def post(self):
eventcode = self.get_argument('eventcode')
eventnickname = self.get_argument('eventnickname')
eventcategory = self.get_argument('eventcategory')
result = createtimesheetevent(eventcode,eventnickname,eventcategory)
resultpath = gettemplatepath('result.html')
if result == 'Fail':
self.render(resultpath,result=result)
else:
self.redirect('/createtimesheetevent')
在get中,我们会获取所有的timesheeteventcategory并传入前端,以便用户选择;在post中,我们也会从表单中获取eventcategory的值传入util函数中。
同样,在createtimesheetevent.html中添加一个下拉框,以便选择当前所有的事件类别。
<!--createtimesheetevent.html-->
<!-...-->
<form class="form-horizontal form-material" action="/createtimesheetevent" method="post" >
<div class="form-group">
<label class="col-md-12">事件名 *</label>
<div class="col-md-12">
<input type="text" class="form-control form-control-line" required=true name="eventcode" id="eventcode">
</div>
<label class="col-md-12">别名 *</label>
<div class="col-md-12">
<input type="text" class="form-control form-control-line" required=true name="eventnickname" id="eventnickname">
</div>
<label class="col-md-12">类别 *</label>
<div class="col-md-12">
<select class="form-control form-control-line" name="eventcategory" id="eventcategory">
{% for category in timesheeteventcategorys %}
<option value="{{ category.eventcategoryname }}">{{ category.eventcategoryname }}</option>
{% end %}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-success">创建/修改类别</button>
</div>
</div>
</form>
<!-...-->
这样,我们就完成了事件类别的相关工作,为请假系统打好了一个小基础。
十二 请假系统的数据库部分
我们的请假系统的成品如下图所示:
在表单中,可以选择请假的类别,在设定好开始时间和结束时间后,系统会自动算出一共请了多少天假,且最小单位是半天;在输入完请假原因后,点击创建即可创建请假申请。
现在,让我们先把所需的表建起来。目前我们只需要一张新表来管理假单,名为Vacation表。
在database目录下建立tblvacation.py文件,输入以下内容:
# database/tblvacation.py
from database.tablebase import Base
from sqlalchemy import Column,String,Integer,Date,Boolean
class Vacation(Base):
__tablename__ = 'vacation'
id = Column(Integer,autoincrement=True,primary_key=True)
# 请假人
username = Column(String,nullable=False)
# 假期类别
vacationcategory = Column(String,nullable=False)
# 起始日期
startdate = Column(Date,nullable=False)
# 起始日期是否为上午
startdateMorning = Column(Boolean,nullable=False)
# 终止日期
enddate = Column(Date,nullable=False)
# 终止日期是否为上午
enddateMorning = Column(Boolean,nullable=False)
# 请假理由
reason = Column(String,nullable=False)
# 假期总时间
timesum = Column(String,nullable=False)
# 审批人
approveuser = Column(String,nullable=False)
# 审批日期
approvedate = Column(Date,nullable=False)
# 状态
state = Column(String,nullable=False)
# 提交日期
applydate = Column(Date,nullable=False)
def __repr__(self):
return '<vacation(username=%s,vacationcategory=%s)>' % (self.username,self.vacationcategory)
然后再次输入熟悉的alembic升版命令:
alembic revison -m "add vacation table"
在新生成的py文件中,改写其upgrade函数:
def upgrade():
op.create_table(
'vacation',
sa.Column('id', sa.Integer, primary_key=True, autoincrement=True),
sa.Column('username', sa.String, nullable=False),
sa.Column('vacationcategory', sa.String, nullable=False),
sa.Column('startdate', sa.Date,nullable=False),
sa.Column('startdateMorning', sa.Boolean,nullable=False),
sa.Column('enddate', sa.Date,nullable=False),
sa.Column('enddateMorning', sa.Boolean,nullable=False),
sa.Column('reason', sa.String, nullable=False)
sa.Column('timesum', sa.String,nullable=False)
sa.Column('approveuser',sa.String,nullable=False)
sa.Column('approvedate', sa.Date, nullable=False)
sa.Column('state', sa.String, nullable=False)
sa.Column('applydate', sa.Date,nullable=False)
)
然后执行upgrade命令,将vacation表建立完毕。此时,我们就完成了请假系统的数据库部分。
在这期博客中,我们实现了创建事件类别的功能,并且为已有的考勤事件添加了事件类别;此外,我们还展示了请假系统的界面,以及完成了vacation表的建立。在下一篇博客中,我将继续介绍请假系统的后端实现,希望大家继续关注~