Flask 实战教程-实现 todoList 包含 mysql 操作
python 安装我就不介绍了可查看安装 Python - Python 教程 - 廖雪峰的官方网站
1.创建项目
1.1 安装 Flask
# 使用 pip 安装 Flask
pip install Flask
1.2 创建 app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def page_home():
return 'home'
if __name__ == '__main__':
app.run(debug=True)
1.3 运行
python app.py
2.路由介绍
2.1 加载普通 html
- 使用 ai 生成一个前端 html 页面,创建/templates/todo.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简洁待办清单</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: #f5f5f5;
min-height: 100vh;
display: flex;
justify-content: center;
padding: 40px 20px;
}
.container {
width: 100%;
max-width: 600px;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
font-size: 2.5rem;
}
.input-section {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
#todoInput {
flex: 1;
padding: 12px 15px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s;
}
#todoInput:focus {
outline: none;
border-color: #3498db;
}
#addBtn {
background: #3498db;
color: white;
border: none;
padding: 12px 25px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s;
}
#addBtn:hover {
background: #2980b9;
}
.todo-list {
list-style: none;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.todo-item {
padding: 15px 20px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
gap: 15px;
transition: all 0.3s;
}
.todo-item:last-child {
border-bottom: none;
}
.todo-item:hover {
background: #f9f9f9;
}
.todo-text {
flex: 1;
font-size: 16px;
color: #333;
}
.todo-item.completed .todo-text {
color: #95a5a6;
text-decoration: line-through;
}
.delete-btn {
background: #e74c3c;
color: white;
border: none;
padding: 5px 12px;
border-radius: 5px;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
}
.todo-item:hover .delete-btn {
opacity: 1;
}
.checkbox {
width: 20px;
height: 20px;
cursor: pointer;
}
@media (max-width: 480px) {
h1 {
font-size: 2rem;
}
#addBtn {
padding: 12px 20px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>待办清单</h1>
<div class="input-section">
<input type="text" id="todoInput" placeholder="添加新的待办事项...">
<button id="addBtn">添加</button>
</div>
<ul class="todo-list" id="todoList"></ul>
</div>
<script>
const todoInput = document.getElementById('todoInput');
const addBtn = document.getElementById('addBtn');
const todoList = document.getElementById('todoList');
let todos = [];
function getTodo () {
fetch('/get/todo').then(res => {
return res.json()
}).then(res => {
console.log('res==>', res)
todos = res.list
renderTodos();
})
}
getTodo()
addBtn.addEventListener('click', addTodo);
todoInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addTodo();
});
function addTodo () {
const text = todoInput.value.trim();
const formData = new FormData()
if (!text) return
formData.append('content', text);
fetch('/add/todo', {
method: 'post',
body: formData
}).then(res => {
return res.json()
}).then(res => {
if (res.status == 200) {
getTodo();
todoInput.value = '';
}
})
}
function renderTodos () {
todoList.innerHTML = todos.map((todo, index) => `
<li class="todo-item ${todo.flag == 1 ? 'completed' : ''}">
<input type="checkbox" class="checkbox" ${todo.flag == 1 ? 'checked' : ''}
onchange="toggleTodo(${todo.id}, ${todo.flag})">
<span class="todo-text">${todo.content}</span>
<button class="delete-btn" onclick="deleteTodo(${todo.id})">删除</button>
</li>
`).join('');
}
function toggleTodo (id, flag) {
fetch('/edit/todo/' + id, {
method: 'put',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ flag: flag == 0 ? 1 : 0 })
}).then(res => {
return res.json()
}).then(res => {
if (res.status == 200) {
getTodo();
}
})
// todos[index].completed = !todos[index].completed;
}
function deleteTodo (id) {
fetch('/del/todo/' + id, {
method: 'delete',
}).then(res => {
return res.json()
}).then(res => {
if (res.status == 200) {
getTodo();
}
})
}
</script>
</body>
</html>
2.2 使用模板语法
- 加载 ai 生成的 todo.html 页面
# app.py
@app.route('/')
def page_home():
return render_template('todo.html')
- 运行查看
2.3 连接数据库
- 安装依赖 sqlalchemy、flask_sqlalchemy
pip install sqlalchemy
pip install flask_sqlalchemy
- 引入依赖、连接数据库
#app.py
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import inspect
import pymysql
from flask import Flask, render_template, request, jsonify
pymysql.install_as_MySQLdb()
app = Flask(__name__)
class Config:
user = 'root'
psw = 'root'
database = 'flask_test'
app.config[
'SQLALCHEMY_DATABASE_URI'] = f'mysql://{user}:{psw}@127.0.0.1:3306/{database}'
# 设置sqlalchemy自动更跟踪数据库
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
# 禁止自动提交数据处理
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = False
db = SQLAlchemy(app)
- 建表与初始化
#app.py
class Todo(db.Model):
__tablename__ = 'todo'
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(60))
flag = db.Column(db.Integer)
def __repr__(self):
return '<Todo %r>' % self.content
def table_exists(table_name):
with app.app_context():
engine = db.get_engine()
inspector = inspect(engine)
return inspector.has_table(table_name)
def create_table():
with app.app_context():
if (table_exists(table_name='todo') == False):
print('创建表……')
db.create_all()
todo = Todo(flag=0, content='Todo')
db.session.add(todo)
db.session.commit()
else:
print('已存在表……')
if __name__ == '__main__':
create_table()
app.run(debug=True)
- 增加获取 todo 列表接口
#app.py
@app.route('/get/todo')
def todu_get():
todo = Todo.query.all()
list = [{
'id': item.id,
'content': item.content,
'flag': item.flag
} for item in todo]
try:
return jsonify({'status': 200, 'list': list})
except Exception as ex:
print(ex)
return jsonify({
'status': 10000,
})
- 进入页面测试
2.4 增删改查功能
- 添加 todo 接口
#app.py
@app.route('/add/todo', methods=['POST'])
def todu_add():
try:
with app.app_context():
content = request.form.get('content')
todo = Todo(flag=0, content=content)
db.session.add(todo)
db.session.commit()
return jsonify({'status': 200, 'msg': '添加成功'})
except Exception as ex:
print(ex)
return jsonify({
'status': 10000,
})
-
测试添加接口
-
增加删除接口
#app.py
@app.route('/del/todo/<int:todo_id>', methods=['DELETE'])
def todu_delt(todo_id):
try:
todo = Todo.query.get(todo_id)
db.session.delete(todo)
db.session.commit()
return jsonify({
'status': 200,
})
except Exception as ex:
print(ex)
return jsonify({'status': 10000})
-
删除接口测试
-
修改接口
#app.py
@app.route('/edit/todo/<int:todo_id>', methods=['PUT'])
def todu_edit(todo_id):
try:
todo = Todo.query.get(todo_id)
if not todo:
return jsonify({'status': 10000})
data = request.get_json()
print('data==>', data)
todo.flag = data.get('flag')
db.session.commit()
return jsonify({
'status': 200,
})
except Exception as ex:
print(ex)
return jsonify({'status': 10000})
- 修改接口测试
3.解析 css、img 等外部文件
- 提取我们的 css 代码到/static/style.css 下
static
-style.css
templates
-todo.html
app.py
- todo.html 加载 css 文件
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
Flask 蓝图
- 创建文件/todocurd/routes.py与/todocurd/_init_.py
static
-style.css
templates
-todo.html
todocurd
-routes.py
-__init__.py
app.py
- 创建蓝图
#/curd/routes.py
from flask import Blueprint, request, jsonify
curd_bp = Blueprint('curd', __name__)
- 注册蓝图
#app.py
app.register_blueprint(
curd_bp,
url_prefix='/',
static_folder='static', # 可选的静态文件配置
template_folder='templates')
3.模块化提取
# mysqlConfig.py
from flask_sqlalchemy import SQLAlchemy
def init(app):
db = SQLAlchemy(app)
class Todo(db.Model):
__tablename__ = 'todo'
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(60))
flag = db.Column(db.Integer)
def __repr__(self):
return '<Todo %r>' % self.content
return Todo, db
- 根目录创建 glo.py,全局变量用于存储数据库对象
# glo.py
def _init():
global _global_dict
_global_dict = {}
def set_value(key, value):
_global_dict[key] = value
def get_value(key, defValue=None):
try:
return _global_dict[key]
except KeyError:
return defValue
from flask import Blueprint, request, jsonify
from glo import get_value
curd_bp = Blueprint('todocurd', __name__)
@curd_bp.route('/get/todo')
def todu_get():
Todo = get_value('Todo')
todo = Todo.query.all()
list = [{
'id': item.id,
'content': item.content,
'flag': item.flag
} for item in todo]
try:
return jsonify({'status': 200, 'list': list})
except Exception as ex:
print(ex)
return jsonify({
'status': 10000,
})
@curd_bp.route('/add/todo', methods=['POST'])
def todu_add():
app = get_value('app')
Todo = get_value('Todo')
db = get_value('db')
try:
with app.app_context():
content = request.form.get('content')
todo = Todo(flag=0, content=content)
db.session.add(todo)
db.session.commit()
return jsonify({'status': 200, 'msg': '添加成功'})
except Exception as ex:
print(ex)
return jsonify({
'status': 10000,
})
@curd_bp.route('/del/todo/<int:todo_id>', methods=['DELETE'])
def todu_delt(todo_id):
app = get_value('app')
Todo = get_value('Todo')
db = get_value('db')
try:
todo = Todo.query.get(todo_id)
db.session.delete(todo)
db.session.commit()
return jsonify({
'status': 200,
})
except Exception as ex:
print(ex)
return jsonify({'status': 10000})
@curd_bp.route('/edit/todo/<int:todo_id>', methods=['PUT'])
def todu_edit(todo_id):
Todo = get_value('Todo')
db = get_value('db')
try:
todo = Todo.query.get(todo_id)
if not todo:
return jsonify({'status': 10000})
data = request.get_json()
print('data==>', data)
todo.flag = data.get('flag')
db.session.commit()
return jsonify({
'status': 200,
})
except Exception as ex:
print(ex)
return jsonify({'status': 10000})
- app.py改写
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import inspect
import pymysql
from flask import Flask, render_template, request, jsonify
from todocurd.routes import curd_bp
from mysqlConfig import init
from glo import _init, set_value
pymysql.install_as_MySQLdb()
app = Flask(__name__)
class Config:
user = 'root'
psw = 'root'
database = 'flask_test'
app.config[
'SQLALCHEMY_DATABASE_URI'] = f'mysql://{user}:{psw}@127.0.0.1:3306/{database}'
# 设置sqlalchemy自动更跟踪数据库
SQLALCHEMY_TRACK_MODIFICATIONS = True
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
# 禁止自动提交数据处理
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = False
_init()
[Todo, db] = init(app)
app.config.from_object(Config)
def table_exists(table_name):
with app.app_context():
engine = db.get_engine()
inspector = inspect(engine)
return inspector.has_table(table_name)
def create_table():
with app.app_context():
if (table_exists(table_name='todo') == False):
print('创建表……')
db.create_all()
todo = Todo(flag=0, content='Todo')
db.session.add(todo)
db.session.commit()
else:
print('已存在表……')
@app.route('/')
def page_home():
return render_template('todo.html')
set_value("Todo", Todo)
set_value("db", db)
set_value("app", app)
app.register_blueprint(
curd_bp,
url_prefix='/',
static_folder='static', # 可选的静态文件配置
template_folder='templates')
if __name__ == '__main__':
create_table()
app.run(debug=True)
- 测试功能
- 目前项目目录
static
-style.css
templates
-todo.html
todocurd
-routes.py
-__init__.py
app.py
glo.py
mysqlConfig.py
依赖记录
pip freeze > requirements.txt
依赖安装(其他用户拉去代码可以安装)
pip install -r requirements.txt