python(基于Flask的任务清单管理系统)

编程入门 行业动态 更新时间:2024-10-24 16:21:51

python(基于Flask的任务清单<a href=https://www.elefans.com/category/jswz/34/1769858.html style=管理系统)"/>

python(基于Flask的任务清单管理系统)

1.目标

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

2.项目介绍

本应用修改自 TodoMVC 的 todo list 应用,使用 Mariadb 作为数据库后端,Bootstrap 作为前端的 Flask 应用。先给它起个好听的名字吧,方便之后称呼。

todo list => (自定义,随便起名称) => todoest

就像一般的 todo list 应用一样,todoest 实现了以下功能:

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

3.项目效果

  • 新建标签页,启动 todoest

  • 打开浏览器访问 http://localhost:8000/

4.技术分析

  • 为什么选择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组件,根据这些组件,可以快速的搭建一个漂亮、功能完备的网站。其中包括以下组件:下拉菜单、按钮组、按钮下拉菜单、导航、导航条、路径导航、分页、排版、缩略图、警告对话框、进度条、媒体对象等

项目成果:

app包:

__ init __:

from flask import  Flask
from flask_sqlalchemy import  SQLAlchemy
from flask_bootstrap import  Bootstrap
from flask_script import Manager
from flask_migrate import  Migrate
from flask_moment import  Moment
import  pymysqlapp = Flask(__name__)# 数据库报错问题
pymysql.install_as_MySQLdb()# 读取配置文件的配置信息
app.config.from_pyfile('../config.py')
db = SQLAlchemy(app)
manager = Manager(app)
bt = Bootstrap(app)
migrate = Migrate(app, db)
moment = Moment(app)

models(数据库表模板):

from app import db
from werkzeug.security import  generate_password_hash, check_password_hash
from datetime import  datetime# 用户和任务的关系: 一对多, 用户是一, 任务是多,
# 用户和分类的关系:一对多,用户是一,任务是多
# 用户表
class User(db.Model):id = db.Column(db.Integer, autoincrement=True, primary_key=True)username = db.Column(db.String(20), unique=True)password_hash = db.Column(db.String(100), nullable=False)email = db.Column(db.String(30), unique=True)add_time = db.Column(db.DateTime, default=datetime.now()) # 账户创建时间# 1).  User添加属性todos; 2). Todo添加属性user;todos = db.relationship('Todo', backref="user")categories = db.relationship('Category', backref='user')@propertydef password(self):"""u.password"""raise  AttributeError("密码属性不可以读取")@password.setterdef password(self, password):"""u.password = xxxxx """self.password_hash = generate_password_hash(password)def verify_password(self, password):"""验证密码是否正确"""return  check_password_hash(self.password_hash, password)def __repr__(self):return  "<User %s>" %(self.username)# 任务和分类的关系: 一对多
# 分类是一, 任务是多, 外键写在多的一端
# 任务表
class Todo(db.Model):id = db.Column(db.Integer, autoincrement=True, primary_key=True)content = db.Column(db.String(100)) # 任务内容status = db.Column(db.Boolean, default=False) # 任务的状态add_time = db.Column(db.DateTime, default=datetime.now())  # 任务创建时间# 任务的类型,关联另外一个表的idcategory_id = db.Column(db.Integer, db.ForeignKey('category.id'))# 任务所属用户;user_id = db.Column(db.Integer, db.ForeignKey('user.id'))def __repr__(self):return  "<Todo %s>" %(self.content[:6])# 分类表
class Category(db.Model):id = db.Column(db.Integer, autoincrement=True, primary_key=True)name = db.Column(db.String(20), unique=True)add_time = db.Column(db.DateTime, default=datetime.now())  # 任务创建时间# 1). Category添加一个属性todos, 2). Todo添加属性category;todos = db.relationship('Todo', backref='category')user_id = db.Column(db.Integer, db.ForeignKey('user.id'))def __repr__(self):return  "<Category %s>" %(self.name)

form(web表单):

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, \ValidationError, SelectField, DateTimeField
from wtforms.validators import DataRequired, Email, Length, EqualTo# 注册表单
from app.models import User, Categoryclass RegisterForm(FlaskForm):email = StringField(label="邮箱",validators=[DataRequired(),Email(),])username = StringField(label="用户名",validators=[DataRequired(),],)password = PasswordField(label='密码',validators=[DataRequired(),Length(6, 12, "密码必须是6-12位")])repassword = PasswordField(label='确认密码',validators=[EqualTo("password", "密码与确认密码不一致")])submit = SubmitField(label="注册")# *****************************************************# 默认情况下validate_username会验证用户名是否正确, 验证的规则, 写在函数里面def validate_username(self, field):# filed.data ==== username表单提交的内容u = User.query.filter_by(username=field.data).first()if u:raise ValidationError("用户名%s已经注册" % (u.username))def validate_email(self, filed):u = User.query.filter_by(email=filed.data).first()if u:raise ValidationError("邮箱%s已经注册" % (u.email))# 登录表单
class LoginForm(FlaskForm):username = StringField(label="用户名",validators=[DataRequired(),],)password = PasswordField(label='密码',validators=[DataRequired(),# Length(6, 12, "密码必须是6-12位")])submit = SubmitField(label="登录")# 关于任务的基类
class TodoForm(FlaskForm):content = StringField(label="任务内容",validators=[DataRequired()])# 任务类型category = SelectField(label="任务类型",coerce=int,choices=[(item.id, item.name) for item in Category.query.all()])
# 添加任务表单(继承任务的基类)
class AddTodoForm(TodoForm):finish_time = DateTimeField(label="任务终止日期")submit = SubmitField(label="添加任务",)
# 编辑任务表单(继承任务的基类)
class EditTodoForm(TodoForm):submit = SubmitField(label="编辑任务",)

views(视图函数):

import json
from functools import wrapsfrom app import app, dbfrom app.forms import RegisterForm, LoginForm, AddTodoForm, EditTodoForm
from flask import render_template, flash, redirect, url_for, session, requestfrom app.models import User, Todo# 定义装饰器,判断用户是否登录
def is_login(f):"""用来判断用户是否登录成功"""@wraps(f)def wrapper(*args, **kwargs):# 判断session对象中是否有seesion['user'],# 如果包含信息, 则登录成功, 可以访问主页;# 如果不包含信息, 则未登录成功, 跳转到登录界面;;if session.get('user', None):return f(*args, **kwargs)else:flash("用户必须登录才能访问%s" % (f.__name__))return redirect(url_for('login'))return wrapper# 网站首页
@app.route('/')
def index():return redirect(url_for('list'))# 注册页面
@app.route('/register/', methods=['POST', 'GET'])
def register():form = RegisterForm()if form.validate_on_submit():# 1. 从前端获取用户输入的值;email = form.email.datausername = form.username.datapassword = form.password.data# 2. 判断用户是否已经存在? 如果返回位None,说明可以注册;u = User.query.filter_by(username=username).first()if u:flash("用户%s已经存在" % (u.username))return redirect(url_for('register'))else:u = User(username=username, email=email)u.password = passworddb.session.add(u)db.session.commit()flash("注册用户%s成功" % (u.username))return redirect(url_for('login'))return render_template('register.html',form=form)# 登录页面
@app.route('/login/', methods=['GET', 'POST'])
def login():form = LoginForm()if form.validate_on_submit():username = form.username.datapassword = form.password.data# 1. 判断用户是否存在?u = User.query.filter_by(username=username).first()if u and u.verify_password(password):session['user_id'] = u.idsession['user'] = u.usernameflash("登录成功!")return redirect(url_for('index'))else:flash("用户名或者密码错误!")return redirect(url_for('login'))return render_template('login.html',form=form)# 注销页面
@app.route('/logout/')
@is_login
def logout():session.pop('user_id', None)session.pop('user', None)return redirect(url_for('login'))# 添加任务
@app.route('/todo/add/', methods=['GET', 'POST'])
@is_login
def todo_add():form = AddTodoForm()if form.validate_on_submit():# 获取用户提交的内容content = form.content.datacategory_id = form.category.data# 添加到数据库中# 用户登录才可以添加任务,todo = Todo(content=content,category_id=category_id,user_id=session.get('user_id'))db.session.add(todo)db.session.commit()flash("任务添加成功")return redirect(url_for('todo_add'))return render_template('todo/add_todo.html',form=form)# 编辑任务
@app.route('/todo/edit/<int:id>/', methods=['GET', 'POST'])
@is_login
def todo_edit(id):form = EditTodoForm()# *****重要: 编辑时需要获取原先任务的信息, 并显示到表单里面;todo = Todo.query.filter_by(id=id).first()form.content.data = todo.contentform.category.data = todo.category_idif form.validate_on_submit():# 更新时获取表单数据一定要使用request.form方法获取, 而form.content.data并不能获取用户更新后提交的表单内容;# print(request.form)# content = form.content.data   # error# category_id = form.category.data   # errorcontent = request.form.get('content')category_id = request.form.get('category')# 更新到数据库里面todo.content = contenttodo.category_id = category_iddb.session.add(todo)db.session.commit()flash("更新任务成功")return redirect(url_for('list'))return render_template('todo/edit_todo.html',form=form)# 删除任务: 根据任务id删除
@app.route('/todo/delete/<int:id>/')
@is_login
def todo_delete(id):todo = Todo.query.filter_by(id=id).first()db.session.delete(todo)db.session.commit()flash("删除任务成功")return redirect(url_for('list'))# 查看任务
@app.route('/todo/list/')
@app.route('/todo/list/<int:page>/')
@is_login
def list(page=1):# 任务显示需要分页# Todo.query.paginate(page, per_page=5)todoPageObj = Todo.query.filter_by(user_id=session.get('user_id')).paginate(page, per_page=app.config['PER_PAGE'])  # 在config.py文件中有设置;return render_template('todo/list_todo.html',todoPageObj=todoPageObj,)# 修改任务状态为完成
@app.route('/todo/done/<int:id>/')
@is_login
def done(id):todo = Todo.query.filter_by(id=id).first()todo.status = Truedb.session.add(todo)db.session.commit()flash("修改状态成功")return redirect(url_for('list'))# 修改任务状态为未完成
@app.route('/todo/undo/<int:id>')
@is_login
def undo(id):todo = Todo.query.filter_by(id=id).first()todo.status = Falsedb.session.add(todo)db.session.commit()flash("修改状态成功")return redirect(url_for('list'))@app.route('/showTodo/')
@is_login
def showTodo():done_count = Todo.query.filter_by(status=True).count()undone_count = Todo.query.filter_by(status=False).count()return render_template('todo/show_todo.html',info={'已完成': done_count,'未完成': undone_count})# 通过echarts在前端显示任务完成与未完成的比例
@app.route('/newShowTodo/')
@is_login
def newShowTodo():return render_template('todo/new_show_todo.html')# 通过前端的ajax中的路由获取该视图函数返回的数据
# 因数据在前端的screpts标签中使用,
# 所以需要将python字典数据类型转换成js能识别的json格式
@app.route('/get_data/')
@is_login
def get_data():done_count = Todo.query.filter_by(status=True).count()undone_count = Todo.query.filter_by(status=False).count()info = {'info': ["已完成", "未完成"],'count': [done_count, undone_count]}# ensure_ascii=False,解决中文编码问题return json.dumps(info, ensure_ascii=False)# 显示cup使用率
@app.route('/disk/')
def get_disk():import psutilcpuInfo = json.dumps({'CPU占有率', psutil.cpu_percent()},ensure_ascii=False)return cpuInfo

static(静态文件):

# css中的main.css:
.navbar {font-size: 130%;background: whitesmoke;margin-top: 10px;padding-top: 5px;box-shadow: 2px 2px 2px 2px lightgray;height: 60px;
}
# js中的echarts.min.js文件:

templates(html页面):
根据相应视图函数的功能编写适合的html页面,可通过bootstrap中文文档上的模板制作美观的页面。
config(配置文件):

# 数据库配置
SQLALCHEMY_DATABASE_URI = 'mysql://root:redhat@localhost/Todo'
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 缓存安全配置,防止csrf(跨站请求伪造)
SECRET_KEY = 'westos'
# 每一页的数量
PER_PAGE = 5

manage(应用程序):

from app import manager, db
# 导入所有视图函数
from app.views import *
from flask_migrate import MigrateCommand
# 添加数据库操作的命令信息;
from app.models import User, Category, Todo# 添加初始化数据库操作命令
@manager.command
def dbinit():"""数据库初始化信息"""db.drop_all()db.create_all()u = User(username='admin', email="admin@qq.com")u.password = 'admin'db.session.add(u)db.session.commit()print("用户%s创建成功......." % (u.username))c = Category(name="学习", user_id=1)db.session.add(c)print("分类%s创建成功...." % (c.name))t = Todo(content="学习Flask", category_id=1, user_id=1)db.session.add(t)print("任务%s添加成功....." % (t.content))db.session.commit()# 添加数据库迁移命令
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':manager.run()

涉及到的相关扩展

Flask-Moment本地化日期和时间

1. 为什么使用Flask-Moment?
  • 如果Web程序的用户来自世界各地,那么就思考如何让Web的世界和当地时间一致。服务器需要统一时间单位,这和用户所在的地理位置无关,所以一般会使用协调时间时(Coordinated Universal Time,UTC)。但是对于用户来说他们想看到的是自己所在的当地时间,而且使用当地惯用的格式。
2. Flask-Moment实现原理?
  • 一个优雅的解决方法就是把时间单位发送给Web浏览器,转换成当地时间,然后渲染。Web浏览器可以更好的更成这一任务,因为他们能获得电脑中的时区和区域设置。
  • 有一个使用JavaScript开发的优秀客户端开源代码库,名为moment.js,它可以在浏览器中渲染日期和时间。Flask-Moment是一个Flask程序扩展,能把moment.js集成到Jinja2模板中。
3. 具体代码?
  1. 在此之前需要安装flask-moment模块,
  2. 之后在app包中的__init__文件中导入该模块:from flask-moment import Moment,
  3. 并实例化moment的对象使其与应用相关联:moment=Moment(app)
  • 模板的编写
    除了moment.js.Flask_Moment还依赖jquery.js,要在HTML文档的引入这连个文件,这样可以选择使用哪个版本,也可以使用扩展提供的辅助函数,从内容分发网络中引入通过测试的加新内容,必须使用JinJa2提供的super()函数。比如可以这样在基模板中的scripts块中引入这个库。
# 在基模板引入moment模块
{% block script %}
{{ super()}}
{{ monent.include_moment()}}
{% endblock %}
  • 编辑视图函数
    为了处理时间,Flask-Moment向模板开放了monent类。所以代码可以把变量current_time(也就是UTC时间传给模板处理)传入模板进行渲染。就这样解决了本地时间问题
@app.route('/')
def index():return render_template('index.html',current_time=datetime.utcnow())
  • 编辑页面逻辑
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}.</p>
  • 参考资料
    Flask-Moment 实现了 moment.js 中的 format() 、 fromNow() 、 fromTime() 、 calendar() 、 valueOf()和 unix() 方法。你可查阅文档(/)学习 moment.js 提供的全部格式化选项。

基于Flask和pyecharts的图形可视化

1.Flask整合Echarts库

Flask 是python web开发的微框架,Echarts酷炫的功能主要是javascript起作用,将两者结合起来,发挥的作用更大。

  • 静态请求实现步骤
    1). 去官网下载Echarts
    2). 引入看Echarts官网文档的教程
  • 动态请求步骤: 使用json和ajax请求
    1). 什么是ajax?
    AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。AJAX 是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
    2). 什么是json?
    JSON:JavaScript Object Notation (JavaScript 对象表示法)。JSON 是存储和交换文本信息的语法。类似 XML。
# ajax中url路由对应的视图函数
# 前端获取后台数据
@app.route('/getdata')
def get_data():language = ['python', 'java', 'c', 'c++', 'c#', 'php']value = ['100', '150', '100', '90', '80', '90']return json.dumps({'language':language,'value':value},ensure_ascii=False) #如果有中文的话,就需要ensure_ascii=False# 编写在scripts标签里面,$(function() {});是$(document).ready(function(){ })的简写 $(function () {// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'));$.ajax({url:'/getdata',success:function (data) {# 相当于python里面的将json格式解析为字典;json_data=JSON.parse(data)console.info(json_data['language'])console.info(json_data['value'])// 指定图表的配置项和数据var option = {title: {text: '学习语言人数统计'},tooltip: {},legend: {data:['销量']},xAxis: {data: json_data['language']},yAxis: {},series: [{name: '销量',type: 'bar',data: json_data['value']}]};// 使用刚指定的配置项和数据显示图表。myChart.setOption(option);}})})

更多推荐

python(基于Flask的任务清单管理系统)

本文发布于:2023-07-28 17:39:34,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1265176.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:管理系统   清单   python   Flask

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!