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

image.png

2.路由介绍

2.1 加载普通 html

  1. 使用 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 使用模板语法

  1. 加载 ai 生成的 todo.html 页面
# app.py
@app.route('/')
def page_home():
    return render_template('todo.html')
  1. 运行查看 image.png

2.3 连接数据库

  1. 安装依赖 sqlalchemy、flask_sqlalchemy
pip install sqlalchemy
pip install flask_sqlalchemy
  1. 引入依赖、连接数据库
#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)
  1. 建表与初始化
#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)
  1. 增加获取 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,
        })
  1. 进入页面测试 image (1).png

2.4 增删改查功能

  1. 添加 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,
        })
  1. 测试添加接口 冰箱进攻中路-烟球2-yasuo.gif

  2. 增加删除接口

#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})
  1. 删除接口测试

  2. 修改接口

#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})
  1. 修改接口测试

image.png

3.解析 css、img 等外部文件

  1. 提取我们的 css 代码到/static/style.css 下
static
  -style.css
templates
  -todo.html
app.py
  1. todo.html 加载 css 文件
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">

Flask 蓝图

  1. 创建文件/todocurd/routes.py与/todocurd/_init_.py
static
  -style.css
templates
  -todo.html
todocurd
  -routes.py
  -__init__.py
app.py
  1. 创建蓝图
#/curd/routes.py
from flask import Blueprint, request, jsonify

curd_bp = Blueprint('curd', __name__)
  1. 注册蓝图
#app.py
app.register_blueprint(
    curd_bp,
    url_prefix='/',
    static_folder='static',  # 可选的静态文件配置
    template_folder='templates')

3.模块化提取

  1. 根目录创建 mysqlConfig.py
# 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
  1. 根目录创建 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
  1. 提取 app.py的内容到/dotocurd/routes.py
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})
  1. 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)
  1. 测试功能

image.png

  1. 目前项目目录
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