基于Flask框架的任务管理系统
基于Flask框架的任务管理系统基本功能:任务的添加、删除、查看、编辑用户登录、注册、登出数据库层任务:任务id号任务名任务添加时间任务的状态(完成/未完成)任务所属部门(外键关联部门department.id)部门部门id编号部门名称部门任务(关联Todo)部门用户(关联User)用户用户id编号用户名用户密码邮箱地址...
·
基于Flask框架的任务管理系统
基本功能:
- 任务的添加、删除、查看、编辑、点击完成
- 用户登录、登出
##数据库层
任务:
- 任务id号
- 任务名
- 任务添加时间
- 任务的状态(完成/未完成)
- 任务所属部门(外键关联部门department.id)
部门
- 部门id编号
- 部门名称
- 部门任务(关联Todo)
- 部门用户(关联User)
用户
- 用户id编号
- 用户名
- 用户密码
- 邮箱地址
- 联系方式
- 用户简介
- 注册时间
- 所属部门
- 用户操作日志
用户登录日志
- 日志编号
- 用户(关联user)
- 登录主机ip
- 登录时间
- 登录地区
models.py
import os
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
#将models绑定app
app=Flask(__name__)
#配置ORM
app.config['SQLALCHEMY_DATABASE_URI']='mysql+pymysql://root:@localhost/TodoProject'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True
#CSRF加密密钥验证,字符变量由24个随机字符组成
app.config['SECRET_KEY']=os.urandom(24)
#绑定Bootstrap前端框架,可以使用框架内简洁的样式
Bootstrap(app)
#实例化db对象
db=SQLAlchemy(app)
class Todo(db.Model):
"""一个关于任务的类"""
id=db.Column(db.Integer,autoincrement=True,primary_key=True) #任务编号
name=db.Column(db.String(50),unique=True) #任务名称
add_time=db.Column(db.DateTime,default=datetime.now()) #创建任务的时间
status=db.Column(db.Boolean,default=False) #任务的状态
#外键关联部门的id
department_id=db.Column(db.Integer,db.ForeignKey('department.id'))
class Department(db.Model):
"""一个关于部门的类"""
id=db.Column(db.Integer,autoincrement=True,primary_key=True) #任务编号
name=db.Column(db.String(50),unique=True)
todos=db.relationship('Todo',backref='department')
users=db.relationship('User',backref='department')
class User(db.Model):
"""一个关于用户的类"""
id=db.Column(db.Integer,autoincrement=True,primary_key=True) #任务编号
name=db.Column(db.String(20),unique=True)
pwd=db.Column(db.String(100))
email=db.Column(db.String(50),unique=True)
phone=db.Column(db.String(20),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'))
userlog=db.relationship('Userlog',backref='user')
class Userlog(db.Model):
"""一个关于用户登录日志的类"""
id=db.Column(db.Integer,autoincrement=True,primary_key=True) #任务编号
user_id=db.Column(db.Integer,db.ForeignKey('user.id'))
ip=db.Column(db.String(100))
add_time=db.Column(db.DateTime,default=datetime.now())
areas=db.Column(db.String(100))
if __name__=='__main__':
#创建数据库表
db.create_all()
##form表单
- 用户登录表单和任务编辑表单
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField,PasswordField
from wtforms.validators import DataRequired,Length
class LoginForm(FlaskForm):
name=StringField(
label="用户名",
validators=[
DataRequired("请输入用户名"),
Length(6,10)
],
render_kw={
'placeholder':"请输入用户名"
}
)
passwd=PasswordField(
label="用户密码",
validators=[
DataRequired("请输入用户密码"),
Length(6,10)
],
render_kw={
'placeholder':"请输入用户密码"
}
)
submit=SubmitField(
render_kw={
'value':'登陆',
'class':'btn btn-success pull-right'
}
)
#任务编辑
class EditForm(FlaskForm):
name=StringField(
label='任务名称',
validators=[
DataRequired()
]
)
department=StringField(
label="所属部门",
validators=[
DataRequired()
]
)
submit=SubmitField(
render_kw={
'value':'完成',
'class':'btn btn-success pull-right'
}
)
##视图函数层
基本内容:
- 首页
- 用户登录
- 用户退出
- 任务列表
- 任务添加
- 任务删除
- 任务编辑
views.py
import json
import time
from functools import wraps
from urllib.request import urlopen
from models import app, Userlog, User, Todo, Department, db
from flask import render_template, redirect, request, url_for, flash, session
from forms import LoginForm,EditForm
from werkzeug.security import check_password_hash,generate_password_hash
#首页
@app.route('/')
def headpage():
return render_template('base.html')
#获取用户登陆主机ip的所在地
def get_ip_area(ip):
url='http://ip.taobao.com/service/getIpInfo.php?ip=%s' %(ip)
json_data=urlopen(url).read().decode('utf-8')
s_data=json.loads(json_data)
country=s_data['data']['country']
if country=='xx':
country==''
city=s_data['data']['city']
if city=='xx':
city==''
return country+city
#判断用户是否登陆,检测会话中有无用户信息
def islogin(f):
@wraps(f)
def wrapper(*args,**kwargs):
if not 'user' in session:
return redirect(url_for('login'))
return f(*args,**kwargs)
return wrapper
#登陆页面
@app.route('/login/',methods=['POST','GET'])
def login():
forms=LoginForm()
if request.method=='POST':
u=User.query.filter_by(name=forms.name.data).first()
pwd=forms.passwd.data
if u and check_password_hash(u.pwd,pwd):
session['user']=u.name
session['user_id']=u.id
userlog=Userlog(
user_id=u.id,
ip=request.remote_addr,
areas=get_ip_area(request.remote_addr)
)
db.session.add(userlog)
db.session.commit()
flash('登陆成功','ok')
time.sleep(2)
return redirect('/list/')
else:
flash('用户名或密码错误','error')
return render_template('login.html',forms=forms)
#任务列表
@app.route('/list/<int:page>',methods=['GET','POST'])
@app.route('/list/')
@islogin
def list(page=1):
todos = Todo.query.paginate(page,per_page=4)
parts = Department.query.all()
return render_template('list.html',todos=todos,parts=parts)
#点击完成
@app.route('/todoup/<int:page>/<int:id>')
@app.route('/todoup/')
@islogin
def todoup(page,id):
todo=Todo.query.filter_by(id=id).first()
todo.status=1
db.session.add(todo)
db.session.commit()
return redirect(url_for('list',page=page))
#点击未完成
@app.route('/tododown/<int:page>/<int:id>')
@app.route('/tododown/')
@islogin
def tododown(page,id):
todo=Todo.query.filter_by(id=id).first()
todo.status=0
db.session.add(todo)
db.session.commit()
return redirect(url_for('list',page=page))
#删除操作
@app.route('/delete/<int:page>/<int:id>')
@islogin
def delete(page,id):
todo=Todo.query.filter_by(id=id).first()
db.session.delete(todo)
db.session.commit()
return redirect(url_for('list',page=page))
#编辑操作
@app.route('/edit/<int:page>/<int:id>',methods=['GET','POST'])
@islogin
def edit(page,id):
forms=EditForm()
todo = Todo.query.filter_by(id=id).first()
oldname = todo.name
oldpart=todo.department_id
if request.method == 'POST':
todo.name=forms.name.data
todo.department_id=forms.department.data
db.session.add(todo)
db.session.commit()
return redirect(url_for('list', page=page))
forms.name.data=oldname
forms.department.data=oldpart
return render_template('edit.html',forms=forms)
#添加任务
@app.route('/add/',methods=['POST'])
@islogin
def add():
todoname=request.form['name']
part=request.form['part']
todo=Todo(name=todoname,department_id=part)
db.session.add(todo)
db.session.commit()
return redirect(url_for('list'))
#用户注销
@app.route('/logout/')
@islogin
def logout():
session.pop('user',None)
session.pop('user_id',None)
return redirect(url_for('login'))
#用户注销
@app.route('/backhead/')
@islogin
def backhead():
session.pop('user',None)
session.pop('user_id',None)
return redirect(url_for('headpage'))
app.run(port='9000')
##模板层
templates
├── base.html #基础模版:导航栏
├── edit.html #编辑任务模版(继承base.html)
├── list.html #任务列表模版(继承base.html)
├── login.html #用户登录模版(继承base.html)
└── macro #自定义的宏,用于分页列表的操作
└── page.html
base.html
{% extends 'bootstrap/base.html' %}
{% block titie %}
首页
{% endblock %}
{% block navbar %}
<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="/"><span style="font-size: x-large">用户管理</span></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><a href="#"> </a></li>
<li>{% block headpage %}
{% endblock %}</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span>
</button>
</form>
<li><a href="/login/" style="font-size: x-large">登录</a></li>
{% block loginout %}
{% endblock %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% endblock %}
{% block content %}
{% endblock %}
login.html
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block titie %}
用户登录
{% endblock %}
{% block content %}
<div class="col-md-4 col-md-offset-4">
<h1 align="center">用户登录</h1>
{% for msg in get_flashed_messages(category_filter='ok') %}
<div class="alert alert-success alert-dismissable">
<button type="button" class="close" data-dismiss="alert"
aria-hidden="true">
×
</button>
{{ msg }}
</div>
{% endfor %}
{% for msg in get_flashed_messages(category_filter='error') %}
<div class="alert alert-success alert-dismissable">
<button type="button" class="close" data-dismiss="alert"
aria-hidden="true">
×
</button>
{{ msg }}
</div>
{% endfor %}
{{ wtf.quick_form(forms) }}
</div>
{% endblock %}
list.html
{% extends 'base.html' %}
{% block titie %}
处理任务
{% endblock %}
{% block navbar %}
<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="/"><span style="font-size: x-large">用户管理</span></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><a href="#"> </a></li>
<li>{% block headpage %}
{% endblock %}</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span>
</button>
</form>
<li><a href="/logout/" style="font-size: x-large">注销</a></li>
<li><a href="/backhead/" style="font-size: x-large">返回首页</a></li>
{% block loginout %}
{% endblock %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% endblock %}
{% block content %}
<br>
<br>
<form action="{{ url_for('add') }}" class="form-horizontal" method="post">
<div class="form-group">
<div class="col-lg-offset-3 col-lg-4 col-sm-5">
<input type="text" class="form-control" placeholder="清添加任务" required="required" name="name">
</div>
<div class="col-lg3 col-sm-2">
<select class="form-control" name="part">
{% for part in parts %}
<option value="{{ part.id }}">{{ part.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg3 col-sm-2">
<input type="submit" class="btn btn-primary" value="完成">
</div>
</div>
</form>
<div class="col-lg-offset-3 col-lg-6">
<table class="table table-striped">
<caption><h1>任务列表</h1></caption>
<thead>
<tr>
<th>任务编号</th>
<th>任务名称</th>
<th>创建时间</th>
<th>所属部门</th>
<th>任务状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for todo in todos.items %}
<tr>
<td>{{ todo.id }}</td>
<td>{{ todo.name }}</td>
<td>{{ todo.add_time }}</td>
<td>{{ todo.department.name }}</td>
<td>
{% if todo.status %}
<a href="{{ url_for('tododown',id=todo.id,page=todos.page) }}" class="btn btn-success" role="button">已完成</a>
{% else %}
<a href="{{ url_for('todoup',id=todo.id,page=todos.page) }}" class="btn btn-warning" role="button">未完成</a>
{% endif %}
</td>
<td>
<a href="{{ url_for('delete',id=todo.id,page=todos.page) }}" class="btn btn-warning" role="button">删除</a>
<a href="{{ url_for('edit',id=todo.id,page=todos.page) }}" class="btn btn-primary" role="button">编辑</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% from 'macro/page.html' import paginate %}
{{ paginate('list',todos) }}
</div>
{% endblock %}
macro/page.html
{% macro paginate(funame,dataObj) %}
<ul class="pagination pull-right">
{# 点击前一页 #}
<li>
{% if dataObj.has_prev %}
<li><a href="{{ url_for(funame,page=dataObj.prev_num) }}">«</a></li>
{% else %}
<li class="disabled"><a href="#">«</a></li>
{% endif %}
</li>
{# 点击首页#}
<li><a href="{{ url_for(funame,page=1) }}">首页</a></li>
<li>
{# 中间页列表,如果页数太多无法显示,则用...代替#}
{% for v in dataObj.iter_pages() %}
{% if v==dataObj.page %}
<li class="active"><a href="{{ url_for(funame,page=v) }}">{{ v }}</a></li>
{% elif v==None %}
<li class="disabled"><a href="#">...</a></li>
{% else %}
<li><a href="{{ url_for(funame,page=v) }}">{{ v }}</a></li>
{% endif %}
{% endfor %}
</li>
{# 点击尾页#}
{# <li><a href="{{ url_for(funame,page=dataObj.) }}">首页</a></li>#}
<li>
{# 点击下一页 #}
{% if dataObj.has_prev %}
<li><a href="{{ url_for(funame,page=dataObj.next_num) }}">»</a></li>
{% else %}
<li class="disabled"><a href="#">»</a></li>
{% endif %}
</li>
</ul>
{% endmacro %}
edit.html
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block titie %}
编辑任务
{% endblock %}
{% block content %}
<div class="col-md-4 col-md-offset-4">
<h1 align="center">编辑任务</h1>
{% for msg in get_flashed_messages(category_filter='ok') %}
<div class="alert alert-success alert-dismissable">
<button type="button" class="close" data-dismiss="alert"
aria-hidden="true">
×
</button>
{{ msg }}
</div>
{% endfor %}
{% for msg in get_flashed_messages(category_filter='error') %}
<div class="alert alert-success alert-dismissable">
<button type="button" class="close" data-dismiss="alert"
aria-hidden="true">
×
</button>
{{ msg }}
</div>
{% endfor %}
{{ wtf.quick_form(forms) }}
</div>
{% endblock %}
更多推荐
已为社区贡献2条内容
所有评论(0)