基于Flask与Mariadb实现任务清单管理

目标

本项目将学习 Mariadb 作为数据库后端,Bootstrap 作为前端的技术栈,并实现一个清单应用。从中我们可以学习 Flask Web 应用框架,及 Mariadb 关系型数据库和 BootStrap web开发框架。

项目介绍

本应用修改自 TodoMVC 的 todo list 应用,使用 Mariadb 作为数据库后端,Bootstrap 作为前端的 Flask 应用。先给它起个好听的名字吧,方便之后称呼。
todo list => (自定义,随便起名称) => todoest
就像一般的 todo list 应用一样,todoest 实现了以下功能:

  • 管理数据库连接
  • 列出所有的 todo 项
  • 创建新的 todo
  • 检索单个 todo
  • 编辑单个 todo 或将其标记为已完成
  • 删除单个 todo

技术分析

  • 为什么选择Flask?
    Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。Flask使用 BSD 授权。
    Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。Flask没有默认使用的数据库、窗体验证工具。
    因此Flask是一个使用Python编写的轻量级Web应用框架。轻巧易扩展,而且够主流,有问题不怕找不到人问,最适合 todoest 这种轻应用了。

  • 为什么选择Mariadb?
    MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。MariaDB虽然被视为MySQL数据库的替代品,但它在扩展功能、存储引擎以及一些新的功能改进方面都强过MySQL。而且从MySQL迁移到MariaDB也是非常简单的.

  • 为什么选择Bootstrap?
    Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML、CSS、JavaScript 开发的简洁、直观、强悍的前端开发框架,使得 Web 开发更加快捷。
    Bootstrap中包含了丰富的Web组件,根据这些组件,可以快速的搭建一个漂亮、功能完备的网站。其中包括以下组件:下拉菜单、按钮组、按钮下拉菜单、导航、导航条、路径导航、分页、排版、缩略图、警告对话框、进度条、媒体对象等

目录结构

基于Flask与Mariadb实现任务清单管理

templates

base.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}首页{% endblock %} | cooffee</title>
    <link rel="stylesheet" href="../static/css/bootstrap.min.css">
    <link rel="stylesheet" href="../static/css/main.css">
    <script src="../static/js/bootstrap.min.js"></script>
</head>
<body>
                     <!--导航栏-->
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="index.html"></a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active" style="padding-bottom: 15px;"><a href="index.html">首页<span class="sr-only">(current)</span></a></li>
        <li><a href="#">国内</a></li>
          <li><a href="#">数读</a></li>
          <li><a href="#">社会</a></li>
          <li><a href="/sysinfo/">系统信息</a></li>
          <li><a href="#">登陆用户</a></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
          {% if 'user' in session %}
           <!--<li><a><span class="glyphicon glyphicon-user"></span></a></li>-->
           <li><a href="#">{{session.user}}</a></li>
           <li><a href="/logout/">注销</a></li>
          {% else %}
           <li><a href="/login/">登陆</a></li>
           <li><a href="/register/">注册</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>
{% block content %}

{% endblock %}
<div class="footer">

        京ICP备11008151号京公网安备11010802014853

</div>

</body>
</html>
index.html:
{% extends "base.html" %}

{% block title %}

{{super()}}

{% endblock %}

{% block content %}
<div class="container container-fluid">
    <div class="row">
        <!--左侧导航-->
        <div class="col-xs-2">
            <div class="list-group left-side">
                <a class="list-group-item left-side-active" href="#">综合</a>
                <a class="list-group-item" href="#">电影</a>
                <a class="list-group-item" href="#">电视剧</a>
                <a class="list-group-item" href="#"> 明星</a>
                <a class="list-group-item" href="#">娱乐</a>
            </div>
        </div>

        <!--中间新闻-->
        <div class="col-xs-7">
            <div class="media">
                <div class="media-left">
                    <a href="news.html">
                        <img class="media-object news-png"
                             src="../static/img/index1.jpg"
                             alt="新闻图片"></a>
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
                    <div class="news-info">
                        <img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
                        <span>王花花</span>.
                        <span>25K评论</span>.
                        <span>7分钟前</span>
                    </div>
                </div>
            </div>
             <div class="media">
                <div class="media-left">
                    <a href="news.html">
                        <img class="media-object news-png"
                             src="../static/img/2.2.jpg"
                             alt="新闻图片"></a>
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
                    <div class="news-info">
                        <img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
                        <span>王花花</span>.
                        <span>25K评论</span>.
                        <span>7分钟前</span>
                    </div>
                </div>
            </div>

             <div class="media">
                <div class="media-left">
                    <a href="news.html">
                        <img class="media-object news-png"
                             src="../static/img/2.jpg"
                             alt="新闻图片"></a>
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
                    <div class="news-info">
                        <img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
                        <span>王花花</span>.
                        <span>25K评论</span>.
                        <span>7分钟前</span>
                    </div>
                </div>
            </div>

             <div class="media">
                <div class="media-left">
                    <a href="news.html">
                        <img class="media-object news-png"
                             src="../static/img/2.2.jpg"
                             alt="新闻图片"></a>
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
                    <div class="news-info">
                        <img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
                        <span>王花花</span>.
                        <span>25K评论</span>.
                        <span>7分钟前</span>
                    </div>
                </div>
            </div>

             <div class="media">
                <div class="media-left">
                    <a href="news.html">
                        <img class="media-object news-png"
                             src="../static/img/index1.jpg"
                             alt="新闻图片"></a>
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
                    <div class="news-info">
                        <img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
                        <span>王花花</span>.
                        <span>25K评论</span>.
                        <span>7分钟前</span>
                    </div>
                </div>
            </div>

             <div class="media">
                <div class="media-left">
                    <a href="news.html">
                        <img class="media-object news-png"
                             src="../static/img/2.2.jpg"
                             alt="新闻图片"></a>
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
                    <div class="news-info">
                        <img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
                        <span>王花花</span>.
                        <span>25K评论</span>.
                        <span>7分钟前</span>
                    </div>
                </div>
            </div>

                      <!--页角-->
            <nav aria-label="Page navigation">
                <ul class="pagination">
                    <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">上一页</span></a>
                    </li>
                    <li class="active"><a href="#">1 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">2 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">3 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">4 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">... <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">10 <span class="sr-only">(current)</span></a></li>
                    <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">下一页</span></a>
                    </li>
                </ul>
            </nav>
        </div>

        <div class="col-xs-3">
                       <!--搜索栏-->
                <div class="input-group input-info">
                  <input type="text" class="form-control" placeholder="搜一下">
                </div>
                     <!--有害信息-->
                <div class="bad-infomation">
                    <table id="bad-table">
                        <tr>
                            <td rowspan="2">
                                <img class="bad-jpg" src="../static/img/4.png">
                            </td>
                            <td id="bad-font1">有害信息举报专区</td>
                        </tr>
                        <tr>
                            <td id="bad-font2">
                                举报电话12377
                            </td>
                        </tr>
                    </table>
                </div>

                    <!--新闻推荐-->
            <div class="list-group hot-news">
                <a href="#" class="list-group-item">
                    <span style="font-size: larger"><b>24小时热闻</b></span>
                </a>
                <a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
                        <span>25K评论</span>
                        <span>7分钟前</span></div></a>
                <a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
                        <span>25K评论</span>
                        <span>7分钟前</span></div></a>
                <a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
                        <span>25K评论</span>
                        <span>7分钟前</span></div></a>
                <a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
                        <span>25K评论</span>
                        <span>7分钟前</span></div></a>
                <a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
                        <span>25K评论</span>
                        <span>7分钟前</span></div></a>

            </div>
        </div>
    </div>
</div>
{% endblock %}

基于Flask与Mariadb实现任务清单管理

login.html:
{% extends "base.html" %}

{% block title %}
登陆
{% endblock %}
{% block content %}
      <!--登陆界面-->
 <div class="container container-small login">
    <h1>登录
      <small>没有账号?<a href="/register/">注册</a></small>
    </h1>
    <form action="/login/" method="post">
      <!--<div class="form-group">-->
        <!--<label>用户名/手机/邮箱</label>-->
        <!--<input name="user" type="text" class="form-control username-text">-->
      <!--</div>-->
      <!--<div class="form-group">-->
        <!--<label>密码</label>-->
        <!--<input name="passwd" type="password" class="form-control username-text">-->
      <!--</div>-->
        {% import 'macro.html' as macro %}

        {{macro.input('text','user','用户名/手机/邮箱')}}
        {{macro.input('password','passwd','密码')}}
      <div class="form-group">
        <button class="btn btn-primary btn-block block-sure" type="submit">登录</button>
      </div>
      <div class="form-group">
        <a href="#">忘记密码?</a>
      </div>
      {% if message %}
        <p style="color: red">{{message}}</p>
      {% endif %}
    </form>
  </div>
{% endblock %}

基于Flask与Mariadb实现任务清单管理
基于Flask与Mariadb实现任务清单管理

singnup.html:
{% extends "base.html" %}

{% block title %}
注册
{% endblock %}
{% block content %}
            <!--注册界面-->
<div class="container container-small login">
    <h1>注册
      <small>没有账号?<a href="/register/">注册</a></small>
    </h1>
    <form action="/register/" method="post">
      {% import 'macro.html' as macro %}

        {{macro.input('text','user','用户名/手机/邮箱')}}
        {{macro.input('password','passwd','密码')}}
      <!--<div class="form-group">-->
        <!--<label>手机</label>-->
        <!--<input type="text" class="form-control username-text">-->
      <!--</div>-->
      <!--&lt;!&ndash;<div class="form-group username-text">&ndash;&gt;-->
        <!--&lt;!&ndash;<label>验证码</label>&ndash;&gt;-->
        <!--&lt;!&ndash;&lt;!&ndash;<div class="input-group">&ndash;&gt;&ndash;&gt;-->
          <!--&lt;!&ndash;&lt;!&ndash;<input type="text" class="form-control">&ndash;&gt;&ndash;&gt;-->
          <!--&lt;!&ndash;&lt;!&ndash;<div class="input-group-btn">&ndash;&gt;&ndash;&gt;-->
            <!--&lt;!&ndash;&lt;!&ndash;<div class="btn btn-default">获取验证码</div>&ndash;&gt;&ndash;&gt;-->
          <!--&lt;!&ndash;&lt;!&ndash;</div>&ndash;&gt;&ndash;&gt;-->
        <!--&lt;!&ndash;&lt;!&ndash;</div>&ndash;&gt;&ndash;&gt;-->
      <!--&lt;!&ndash;</div>&ndash;&gt;-->
      <!--<div class="form-group">-->
        <!--<label>密码</label>-->
        <!--<input type="password" class="form-control username-text">-->
      <!--</div>-->
      <div class="form-group">
        <button class="btn btn-primary btn-block block-sure" type="submit">注册</button>
      </div>
      <div class="form-group">
        注册咖啡或浮云即代表您同意<a href="#">咖啡或浮云服务条款</a>
      </div>
      {% if message %}
        <p style="color: red">{{message}}</p>
      {% endif %}
    </form>
  </div>
{% endblock %}

基于Flask与Mariadb实现任务清单管理

macro.html:
{% macro input(type,name,text) %}
 <div class="form-group">
        <label>{{text}}</label>
        <input name={{name}} type={{type}} class="form-control username-text">
      </div>
{% endmacro %}
sysinfo.html:
{% extends 'base.html' %}


{% block title %} 系统信息 {% endblock %}


{% block content %}



{#    表格内容----bootstrap #}
<table class="table table-striped" style="width: 50%; margin: auto " >
    <tr>
        <td><b style="font-size: large">系统信息</b></td>
        <td></td>
    </tr>
    <tr>
        <td>主机名</td>
        <td>{{ hostname }}</td>
    </tr>
    <tr>
        <td>内核名称</td>
        <td>{{ sysname }}</td>
    </tr>
    <tr>
        <td>发行版本号</td>
        <td>{{ release }}</td>
    </tr>
    <tr>
        <td>内核版本</td>
        <td>{{ version}}</td>
    </tr>
    <tr>
        <td>系统构架</td>
        <td>{{ machine }}</td>
    </tr>
    <tr>
        <td>现在时间</td>
        <td>{{ now_time }}</td>
    </tr>
    <tr>
        <td>开机时间</td>
        <td>{{ boot_time }}</td>
    </tr>
    <tr>
        <td>运行时间</td>
        <td>{{ delta_time }}</td>
    </tr>
</table>

{% endblock %}

基于Flask与Mariadb实现任务清单管理

404.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2 style="color: red;">404: 页面未找到</h2>

</body>
</html>

程序

models.py:

连接数据库

import pymysql
conn = pymysql.connect(
    host='172.25.254.78',
    user='cooffee',
    password='cooffee',
    charset='utf8',
    db='cooffee'
    )

cur = conn.cursor()
def isUserExist(username):
    sqli = "select * from flaskdata where name='%s'" %(username)
    res = cur.execute(sqli)
    if res == 0:
        return False
    else:
        return True
def isPasswdOk(username,passwd):
    sqli = sqli = "select * from flaskdata where name='%s' and passwd='%s'" %(username, passwd)
    res = cur.execute(sqli)
    if res == 0:
        return False
    else:
        return True

def addUser(username,passwd):
    sqli = "insert into flaskdata (name, passwd) values('%s', '%s')" %(username, passwd)
    try:
        res= cur.execute(sqli)
        conn.commit()
    except Exception as e:
        conn.rollback()
        return e
if __name__ == "__main__":
    addUser('root','root')
    print(isUserExist('root'))
    print(isPasswdOk('root','root'))

基于Flask与Mariadb实现任务清单管理

run.py:
import random
import functools

import os
from datetime import datetime

import psutil as psutil
from flask import Flask, request, render_template, redirect, url_for, abort, session
from models import isPasswdOk, isUserExist, addUser
app = Flask(__name__)
app.config['SECRET_KEY'] = random._urandom(24)
def is_login(f):
    """判断用户是否登陆的装饰器"""
    @functools.wraps(f)
    def wrapper(*args,**kwargs):
     # run函数代码里面, 如果登陆, session加入user, passwd两个key值;
        # run函数代码里面, 如果注销, session删除user, passwd两个key值;
        # 如果没有登陆成功, 则跳转到登陆界面
        if 'user' not in session:
            return redirect(url_for('login'))
            # 如果用户是登陆状态, 则访问哪个路由, 就执行哪个路由对应的视图函数;
        return f(*args,**kwargs)
    return wrapper

# 用户主页
@app.route('/')
def index():
    return render_template('index.html')

# 用户登陆按钮
@app.route('/login/',methods=['GET','POST'])
def login():
    if request.method == 'POST':
        print(request.form)
        user = request.form['user']
        passwd = request.form['passwd']
        if isPasswdOk(user,passwd):
        # 将用户名和密码信息存储到session中;
            session['user']=user
            session['passwd']=passwd
            return redirect(url_for('index'))
        else:
            return render_template('login.html',message='用户名或者密码错误')
    else:
        return render_template('login.html')
# 用户注销
@app.route('/logout/')
def logout():
    session.pop('user',None)
    session.pop('passwd',None)
    return redirect(url_for('index'))
# 用户注册
@app.route('/register/',methods=['GET','POST'])
def register():
    if request.method == 'POST':
        user = request.form['user']
        passwd = request.form['passwd']
        if isUserExist(user):
            message = "用户已经存在"
            return render_template('signup.html',message=message)
        else:
            addUser(user,passwd)
            return redirect(url_for('login'))
    else:
        return render_template('signup.html')

@app.route('/sysinfo/')
@is_login
def sysinfo():
    info = os.uname()
    boot_time = psutil.boot_time()
    boot_time = datetime.fromtimestamp(boot_time)
    now_time = datetime.now()
    delta_time = now_time - boot_time
    delta_time = str(delta_time).split('.')[0]
    return render_template('sysinfo.html',
                           hostname=info.nodename,
                           sysname=info.sysname,
                           release=info.release,
                           version= info.version,
                           machine=info.machine,
                           now_time=str(now_time).split('.')[0],
                           boot_time=boot_time,
                           delta_time=delta_time
                           )
# 404异常处理: 类似于捕获异常
@app.errorhandler(404)
def not_found(e):
    return render_template('404.html')

app.run(port=9002)