本项目将学习 Mariadb 作为数据库后端,Bootstrap 作为前端的技术栈,并实现一个清单应用。从中我们可以学习 Flask Web 应用框架,及 Mariadb 关系型数据库和 BootStrap web开发框架。
本应用修改自 TodoMVC 的 todo list 应用,使用 Mariadb 作为数据库后端,Bootstrap 作为前端的 Flask 应用。先给它起个好听的名字吧,方便之后称呼。
todo list => (自定义,随便起名称) => todoest
就像一般的 todo list 应用一样,todoest 实现了以下功能:
为什么选择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组件,根据这些组件,可以快速的搭建一个漂亮、功能完备的网站。其中包括以下组件:下拉菜单、按钮组、按钮下拉菜单、导航、导航条、路径导航、分页、排版、缩略图、警告对话框、进度条、媒体对象等
{% extends "bootstrap/base.html" %}
{% block styles %}
{{ super() }}
{% endblock %}
{% block navbar %}
{% endblock %}
{% block content %}
{% block newcontent %}
{% endblock %}
{% block footer %}
{% endblock %}
{% endblock %}
{% extends 'base.html' %}
{% block newcontent %}
{% endblock %}
{% extends 'bootstrap/base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block content %}
{% endblock %}
{% macro paginate(fname,todos) %}
{# 判断是否由上一页, #}
{% if todos.has_prev %}
- 前一页
{% else %}
- 前一页
{% endif %}
{# 根据从数据库中查询的数据, 来确定分页的个数, 使用for循环 #}
{# 返回一个迭代器, 如果有100页, 薄嗯不会返回100个数字, 而是1 2 。。。。 99 100 #}
{% for page in todos.iter_pages(right_current=2) %}
{# page: 要生成html代码的页数, todos.page: 用户希望显示数据的页数 #}
{% for page in todos.iter_pages(right_current=2) %}
{% if page == todos.page %}
- {{ page }}
{% elif page == None %}
- ...
{% else %}
- {{ page }}
{% endif %}
{% endfor %}
{% if todos.has_next %}
- 后一页
{% else %}
- 后一页
{% endif %}
{% endmacro %}
from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField,SubmitField
from wtforms.validators import DataRequired,Length
class LoginForm(FlaskForm):
user = StringField(
label="用户名/邮箱/手机号",
validators=[
DataRequired(message="请输入用户名"),
Length(7,9,message="位数为7-9位")
]
)
passwd = PasswordField(
label="密码",
validators=[
DataRequired(),
Length(7,message="位数为7")
]
)
sumbit = SubmitField(
label="登陆"
)
import random
from datetime import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"]='mysql+pymysql://cooffee:[email protected]/Todo'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
db = SQLAlchemy(app)
bootstrap = Bootstrap(app)
app.config["SECRET_KEY"] = random._urandom(24)
# 1). 面向对象方式创建表
# 2). 分析关系:
# 部门和用户: 一对多
# 用户和任务: 一对多的关系
# 用户和用户登录日志: 一对多的关系
# 实现一对多(Role(1): User(n))的关系
# - 多的一端写外键
# - 少的一端写反向引用
class User(db.Model):
id = db.Column(db.Integer,autoincrement=True,primary_key=True)
name= db.Column(db.String(50),unique=True,nullable=Flask)
pwd = db.Column(db.String(100))
email = db.Column(db.String(30), unique=True)
phone = db.Column(db.String(30), unique=True)
info = db.Column(db.Text)
add_time = db.Column(db.DateTime, default=datetime.now())
department_id = db.Column(db.Integer, db.ForeignKey('department.id'))
todos = db.relationship('Todo', backref='user')
log = db.relationship('Userlog',backref='user')
class Department(db.Model):
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.String(50), unique=True, nullable=Flask)
users = db.relationship('User', backref='department')
todos = db.relationship('Todo', backref='department')
class Todo(db.Model):
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.String(200), nullable=False)
add_time = db.Column(db.DateTime, default=datetime.now())
status = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
department_id = db.Column(db.Integer, db.ForeignKey('department.id'))
class Userlog(db.Model):
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
add_time = db.Column(db.DateTime, default=datetime.now())
ip = db.Column(db.String(200), nullable=False)
area = db.Column(db.String(200))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
if __name__ == "__main__":
db.create_all()
parts = ['开发部', '运维部', '人事部']
partObj = [Department(name=part) for part in parts]
db.session.add_all(partObj)
db.session.commit()
u1 = User(name="cooffee1", pwd='cooffee', department_id=1)
db.session.add(u1)
db.session.commit()
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell,Manager
from models import app,db,User,Department
manager = Manager(app)
migrate = Migrate(app,db)
manager.add_command('db',MigrateCommand)
@manager.command
def showUser():
"""显示所有的用户"""
users = User.query.all()
print(users[:5])
@manager.command
def showDepart():
"""显示所有的部门"""
deaprts = Department.query.all()
print(deaprts)
@manager.option('-n', '--name', help="部门名称")
def add_depart(name):
try:
depart1 = Department(name=name)
db.session.add(depart1)
db.session.commit()
except Exception:
print("创建部门失败!")
else:
print("创建部门%s成功!" %(name))
if __name__=="__main__":
manager.run()
import functools
from flask import render_template, request, redirect, url_for, session
from forms import LoginForm
from models import app, Todo, db, Department, User
def is_login(f):
"""判断用户是否登陆的装饰器"""
@functools.wraps(f)
def wrapper(*args,**kwargs):
if 'user' not in session:
return redirect(url_for('login'))
return f(*args,**kwargs)
return wrapper
@app.route('/')
def index():
return render_template('base.html')
@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = form.data['user']
passwd = form.data['passwd']
u = User.query.filter_by(name=user).first()
if u and passwd == u.pwd:
session['user'] = user
session['passwd'] = passwd
return redirect(url_for('todo_list'))
else:
return render_template('login.html', message='用户名或者密码错误',form=form)
else:
return render_template('login.html',form=form)
@app.route('/logout/')
def logout():
session.pop('user',None)
session.pop('passwd',None)
return redirect(url_for('index'))
@app.route('/todo/add/',methods=['POST'])
def todo_add():
name = request.form['todo_name']
part = request.form['part']
todo = Todo(name=name,department_id=part,user_id=1)
db.session.add(todo)
db.session.commit()
return redirect(url_for("todo_list"))
@app.route('/list/')
@app.route('/list//')
@is_login
def todo_list(page=1):
todos = Todo.query.paginate(page,5)
parts = Department.query.all()
return render_template('list.html',
todos = todos,
parts=parts)
@app.route('/todo/undo//')
def undo(id):
todo = Todo.query.filter_by(id=id).first()
todo.status = False
db.session.commit()
return redirect(url_for('todo_list'))
@app.route('/todo/done//')
def done(id):
todo = Todo.query.filter_by(id=id).first()
todo.status = True
db.session.commit()
return redirect(url_for('todo_list'))
@app.route('/todo/delete//')
def delete(id):
todo = Todo.query.filter_by(id=id).first()
db.session.delete(todo)
db.session.commit()
return redirect(url_for('todo_list'))import functools
from flask import render_template, request, redirect, url_for, session
from forms import LoginForm
from models import app, Todo, db, Department, User
def is_login(f):
"""判断用户是否登陆的装饰器"""
@functools.wraps(f)
def wrapper(*args,**kwargs):
if 'user' not in session:
return redirect(url_for('login'))
return f(*args,**kwargs)
return wrapper
@app.route('/')
def index():
return render_template('base.html')
@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = form.data['user']
passwd = form.data['passwd']
u = User.query.filter_by(name=user).first()
if u and passwd == u.pwd:
session['user'] = user
session['passwd'] = passwd
return redirect(url_for('todo_list'))
else:
return render_template('login.html', message='用户名或者密码错误',form=form)
else:
return render_template('login.html',form=form)
@app.route('/logout/')
def logout():
session.pop('user',None)
session.pop('passwd',None)
return redirect(url_for('index'))
@app.route('/todo/add/',methods=['POST'])
def todo_add():
name = request.form['todo_name']
part = request.form['part']
todo = Todo(name=name,department_id=part,user_id=1)
db.session.add(todo)
db.session.commit()
return redirect(url_for("todo_list"))
@app.route('/list/')
@app.route('/list//')
@is_login
def todo_list(page=1):
todos = Todo.query.paginate(page,5)
parts = Department.query.all()
return render_template('list.html',
todos = todos,
parts=parts)
@app.route('/todo/undo//')
def undo(id):
todo = Todo.query.filter_by(id=id).first()
todo.status = False
db.session.commit()
return redirect(url_for('todo_list'))
@app.route('/todo/done//')
def done(id):
todo = Todo.query.filter_by(id=id).first()
todo.status = True
db.session.commit()
return redirect(url_for('todo_list'))
@app.route('/todo/delete//')
def delete(id):
todo = Todo.query.filter_by(id=id).first()
db.session.delete(todo)
db.session.commit()
return redirect(url_for('todo_list'))
from views import *
if __name__ == '__main__':
app.run()