Tornado笔记——用Tornado搭建假单统计系统(三)

在上一篇博客里,我们实现了建立用户组的功能,现在让我们来注册我们的第一个用户,并实现一个初始化系统的功能。

七 用户注册

我们的用户注册界面长这个样子:

这个页面包含一个简单的表单,可供用户输入用户名、密码、Email以及选择用户组。

这次我们先写后端函数,我们需要两个util函数来完成用户注册功能:createuser和encryption。前者用于根据参数来创建用户,而后者实现对密码的加密功能,最终存放在数据库中的是加密后的密码。

# util/users/userutil.py
# ...
import hashlib
# ...

def validateemail(email):
    pattern_email = re.compile(r'^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}$')
    if pattern_email.match(email):
        return True
    return False

def encryption(password):
    m = hashlib.md5()
    byte_password = bytes(password,encoding='utf-8')
    m.update(byte_password)
    newpassword = m.hexdigest()
    return newpassword

def createuser(username,password,email,usergroup):
    result = 'Fail'
    if not validateemail(email):
        return result
    user = session.query(User).filter(User.username == username).first()
    if user is not None:
        return result
    user = session.query(User).filter(User.email == email).first()
    if user is not None:
        return result
    if usergroup == 'Root' or usergroup == 'Visitor':
        return result
    # user和email都不重复,则创建user
    password = encryption(password)
    newuser = User(username=username,password=password,email=email,usergroup=usergroup,state='WaitForApprove',registerdate=date.today(),
                   lastlogintime=datetime.now())
    result = insertdata(newuser)
    return result

在encyption函数中,我们对传入的密码做MD5,使得最终存放在数据库中的值是密码的MD5值;在createuser函数中,我们要使用validateemail函数对用户输入的email地址进行校验,如果是合法的email地址,则再根据用户名和email地址分别查询数据库,若都不重复,则根据这些参数来创建用户。此外,我们这里特意排除了Root和Visitor用户组,不允许用户注册为这两个用户组的用户。注意,对密码要先调用encyption函数进行一下加密。新注册用户的状态为WaitForApprove,需要之后得到管理员的批准。

让我们回到main.py,实现注册功能的RequestHandler:

# server/main.py
# ...
class Register(BaseHandler):
    def get(self):
        registerpath = gettemplatepath('register.html')
        usergroups = getallusergroup()
        self.render(registerpath,usergroups=usergroups)

    def post(self):
        username = self.get_argument('username')
        password = self.get_argument('password')
        email = self.get_argument('email')
        usergroup = self.get_argument('usergroup')
        result = createuser(username,password,email,usergroup)
        resultpath = gettemplatepath('result.html')
        if result == 'Success':
            result = '注册成功!'
        else:
            result = '注册失败!'
        self.render(resultpath, result=result)

def make_app():
    routelist = [
        # ...
        (r"/createusergroup",CreateUserGroup),
        # ...
    ]

这个get函数非常简单,通过调用getallusergroup拿到所有可选的(排除Root和Visitor)用户组,并将其渲染到页面中,作为下拉列表的选项;而post函数则是多次调用get_argument方法拿到表单的值,并调用createuser函数来建立用户;在创建好后,根据注册成功与否显示不同的提示信息。最后,别忘了把它加到路由列表中。

对应的register.html如下: 

<!--register.html-->

        {% 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">
                                <form class="form-horizontal form-material" method="post" action="/register" >
                                    <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="username" id="username">
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label for="example-email" class="col-md-12">密码 *</label>
                                        <div class="col-md-12">
                                            <input type="password" class="form-control form-control-line" required=true name="password" id="password">
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-12">Email *</label>
                                        <div class="col-md-12">
                                            <input type="text" class="form-control form-control-line" required=true name="email" id="email">
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-sm-12">用户组 *</label>
                                        <div class="col-sm-12">
                                            <select class="form-control form-control-line" name="usergroup" id="usergroup" >
                                                {% for usergroup in usergroups %}
                                                <option>{{ escape(usergroup.groupname) }}</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>
                            </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 %}

这里要注意的点是,在select表单元素中,我们使用{% for %}循环,将后端传入的usergroups的每个元素作为了下拉框的选择项。{% for %}循环是前端模板中非常常用的关键词,其语法和python语法一致,可循环读可迭代对象的元素值,通常用于建立表格以及作为下拉框的选择项。 

现在我们注册了第一个用户,注意这里的email是合法的地址但不是存在的地址,下面让我们看看登录功能。

 

八 用户登录

与注册界面一样,我们的用户登录界面也是个非常简单的表单:

 这个表单包含三项元素:用户名、密码以及一个是否记住用户的单选框。当用户上次登录选择了“记住我”后,在下次来到这个页面时,应自动填入用户名和密码。

用户登录的util函数很简单,根据用户名以及加密后的密码去数据库查询有无匹配的用户,若有,则修改它的lastlogintime值,否则返回Fail。

# util/users/userutil.py
# ...
from sqlalchemy import and_
# ...
def loginuser(username,password):
    password = encryption(password)
    user = session.query(User).filter(and_(User.username == username,User.password == password)).first()
    result = 'Fail'
    if type(user) is User:
        user.lastlogintime = datetime.now()
        result = insertdata(user)
    return result

它的Handler稍微复杂一些,主要是涉及到了cookie的设置:

# server/main.py
# ...
class Login(BaseHandler):
    def get(self):
        loginpath = gettemplatepath('login.html')
        loginusername = self.get_secure_cookie('loginusername')
        loginuserpassword = self.get_secure_cookie('loginuserpassword')
        remembermevalue = self.get_secure_cookie('rememberme')
        remembermevalue = str(remembermevalue,encoding='utf-8')
        if loginusername is None and loginuserpassword is None:
            loginusername = ''
            loginuserpassword = ''
        self.render(loginpath,loginusername=loginusername,loginuserpassword=loginuserpassword,remembermevalue=remembermevalue)

    def post(self):
        username = self.get_argument('username')
        password = self.get_argument('password')
        rememberme = self.get_argument('rememberme')
        if rememberme == 'on':
            self.set_secure_cookie('loginusername',username)
            self.set_secure_cookie('loginuserpassword',password)
            self.set_secure_cookie('rememberme','T')
        result = loginuser(username,password)
        resultpath = gettemplatepath('result.html')
        if result == 'Success':
            result = '登录成功!'
            self.set_secure_cookie('currentuser',username)
            self.redirect('/')
        else:
            result = '用户名或密码错误!'
            self.render(resultpath, result=result)
# ...
def make_app():
    routelist = [
        # ...
        (r"/login",Login),
        # ...
    ]
    # ...

 在Login的get方法中,会尝试去取loginusername、loginuserpassword和rememberme这三个cookie的值,并将其值反映到表单上;而在post方法中,则是根据表单中“记住我”是否被勾上而设置以上三个cookie的值。若根据输入的用户名和密码成功登录,则将当前用户的用户名放在currentuser中,表示该用户处于登录状态。在成功登录后,使用redirect方法重定向到主页,若登录失败,则提示错误信息。

前端页面代码如下:

<!--login.html-->
        {% 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">
                                <form class="form-horizontal form-material" method="post" action="/login" >
                                    <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="username" id="username" value="{{ loginusername }}">
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-12">密码</label>
                                        <div class="col-md-12">
                                            <input type="password" class="form-control form-control-line" required=true name="password" id="password" value="{{ loginuserpassword }}">
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="col-sm-12">
                                            {% if remembermevalue == "T" %}
                                            <input type="checkbox" class="checkbox checkbox-circle" name="rememberme" id="rememberme" checked>记住我</input>
                                            {% else %}
                                            <input type="checkbox" class="checkbox checkbox-circle" name="rememberme" id="rememberme">记住我</input>
                                            {% end %}
                                        </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 %}

这里值得一提的是,我们可以使用后端传进来的变量作为表单的属性值,如用户名和密码的value值就来自于后端的传入;而对于checkbox,决定其打勾与否的属性名叫checked,这里也是使用{% if %}来生成打勾与否的两种checkbox。

在登录后,我们的用户名会显示在网站右上角。

九 系统初始化

对于我们这个系统而言,最基本的用户组有两个:Root组和Visitor组。Root组毫无疑问,是根用户组,其下也只有一个用户Root;而Visitor组作为未登录用户的组,拥有最少的权限。

我们打开util/users/userutil.py,编写inituser和hasinit函数。前者用于创建Root用户,创建Root和Visitor用户组,而后者用于判断系统是否已初始化。

# util/users/userutil.py
# ...
def inituser():
    # 创建Root用户组以及Root用户
    usergroup = session.query(UserGroup).filter(UserGroup.groupname == 'Root').first()
    result = 'Fail'
    if usergroup is None:
        # 用户组不存在,创建
        newusergroup = UserGroup(groupname='Root', createdate=date.today())
        result = insertdata(newusergroup)
        if result == 'Success':
            user = session.query(User).filter(User.username == 'Root').first()
            if user is None:
                rootuser = User(username='Root',password=encryption('123456'),email='<email地址>',usergroup='Root',state='Approved',registerdate=date.today(),
                   lastlogintime=datetime.now())
                result = insertdata(rootuser)
                if result == 'Success':
                    usergroup = session.query(UserGroup).filter(UserGroup.groupname == 'Visitor').first()
                    if usergroup is None:
                        newusergroup = UserGroup(groupname='Visitor', createdate=date.today())
                        result = insertdata(newusergroup)
                        if result == 'Success':
                            print('系统初始化完成')
    return result


def hasinit():
    result = False
    usergroup = session.query(UserGroup).filter(UserGroup.groupname == 'Root').first()
    if type(usergroup) is not UserGroup:
        return result
    else:
        rootuser = session.query(User).filter(User.username == 'Root').first()
        if type(rootuser) is not User:
            return result
        else:
            result = True
    return result

在inituser中,我们依次判断Root用户组、Root用户和Visitor用户组是否存在,若都不存在,则依次创建,并返回初始化结果。这里要注意的是,由于之前的createuser不允许建立Root组的用户,因此我们需要使用直接建立数据库对象的方式来建立Root用户,这里要记得对密码加密;而hasinit主要判断Root用户组和Root用户是否存在,若不存在,则需要在主函数中调用inituser函数来进行初始化:

# server/main.py
# ...

if __name__ == '__main__':
    init_result = hasinit()
    if init_result == False:
        result = inituser()
        if result == 'Fail':
            print('初始化网站失败!')
            exit(0)
    app = make_app()
    port = int(getconfig('PORT'))
    app.listen(port)
    mainserver = tornado.ioloop.IOLoop.current()
    mainserver.start()

现在,当我们第一次运行系统时,就会自动创建Root用户以及Root和Visitor用户组。

在这篇博客中,我们进一步完善了现有的用户系统,实现了注册、登录以及初始化网站功能。在之后的博客中,将继续介绍用户系统剩余的部分:用户管理以及查看用户信息,然后我们就将进入假单及考勤部分的开发,希望大家继续关注~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值