Python Flask 是一个轻量级的 Web 应用框架,也被称为微框架。它以简洁、灵活和易于上手的特点而受到开发者的喜爱。
核心特点
轻量级:Flask 核心代码简洁,仅包含 Web 开发的基本功能,不强制使用特定的数据库、模板引擎等,开发者可按需选择适合的扩展。
灵活性高:可以自由选择使用的数据库(如 MySQL、SQLite)、模板引擎(如 Jinja2)和其他工具,方便根据项目需求进行定制。
易于上手:对于初学者而言,Flask 的文档清晰易懂,代码结构简单,能够快速搭建起一个 Web 应用。
基本组件
路由系统:用于将 URL 映射到对应的 Python 函数,当用户访问特定 URL 时,执行相应的函数并返回结果。
请求和响应对象:在处理 Web 请求时,Flask 提供了request对象用于获取客户端发送的请求信息,如表单数据、URL 参数等;同时,使用response对象返回响应内容给客户端。
模板引擎:默认使用 Jinja2 作为模板引擎,它允许将 Python 代码和 HTML 模板结合,方便生成动态的 HTML 页面。
会话管理:支持会话(session)管理,通过会话可以在多个请求之间保存用户的状态信息,如用户登录状态。
1、创建新的anaconda环境
conda create --name todoenv python=3.10
创建了todoenv这个conda环境,我们查看一下有已有的环境
conda env list
二、创建项目文件
项目结构如下:
todoapp/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── index.html
│ │ └── edit.html
│ └── static/
│ └── style.css
├── config.py
├── requirements.txt
└── run.py
安装依赖:requirements.txt,通过
pip install -r requirements.txt
一键安装依赖
Flask==3.0.2
Flask-SQLAlchemy==3.1.1
Flask-Migrate==4.0.5
python-dotenv==1.0.1
配置文件:config.py
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key-here'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
应用工厂:app/__init.py__
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
from app.routes import bp
app.register_blueprint(bp)
return app
数据模型(Model):app/models.py
from datetime import datetime
from app import db
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.String(200))
completed = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<Todo {self.title}>'
路由和视图(Controller):app/routes.py
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from app.models import Todo
from app import db
bp = Blueprint('main', __name__)
@bp.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
title = request.form['title']
description = request.form['description']
new_todo = Todo(title=title, description=description)
db.session.add(new_todo)
db.session.commit()
return redirect(url_for('main.index'))
todos = Todo.query.order_by(Todo.created_at.desc()).all()
return render_template('index.html', todos=todos)
@bp.route('/edit/<int:id>', methods=['GET', 'POST'])
def edit(id):
todo = Todo.query.get_or_404(id)
if request.method == 'POST':
todo.title = request.form['title']
todo.description = request.form['description']
todo.completed = 'completed' in request.form
db.session.commit()
return redirect(url_for('main.index'))
return render_template('edit.html', todo=todo)
@bp.route('/delete/<int:id>')
def delete(id):
todo = Todo.query.get_or_404(id)
db.session.delete(todo)
db.session.commit()
return redirect(url_for('main.index'))
# API 端点示例
@bp.route('/api/todos', methods=['GET'])
def get_todos():
todos = Todo.query.all()
return jsonify([{
'id': todo.id,
'title': todo.title,
'description': todo.description,
'completed': todo.completed
} for todo in todos])
编辑模板:app/templates/edit.htm
{% extends "base.html" %}
{% block content %}
<h1 class="mb-4">Edit Todo</h1>
<form method="POST">
<div class="mb-3">
<label class="form-label">Title</label>
<input type="text"
class="form-control"
name="title"
value="{{ todo.title }}"
required>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control"
name="description"
rows="3">{{ todo.description }}</textarea>
</div>
<div class="mb-3 form-check">
<input type="checkbox"
class="form-check-input"
name="completed"
{% if todo.completed %}checked{% endif %}>
<label class="form-check-label">Completed</label>
</div>
<button type="submit" class="btn btn-primary">Update</button>
<a href="{{ url_for('main.index') }}" class="btn btn-secondary">Cancel</a>
</form>
{% endblock %}
样式文件:app/static/style.css
/* 基础样式 */
body {
background-color: #f8f9fa;
padding: 20px;
}
.container {
max-width: 800px;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
/* Todo项样式 */
.list-group-item {
margin-bottom: 10px;
transition: all 0.3s ease;
}
.list-group-item-success {
background-color: #d4edda;
border-color: #c3e6cb;
}
/* 表单样式 */
form {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.form-control:focus {
box-shadow: none;
border-color: #80bdff;
}
/* 按钮间距 */
.btn {
margin-right: 8px;
}
/* 完成状态文字 */
.list-group-item-success h5 {
text-decoration: line-through;
color: #155724;
}
/* 时间戳样式 */
.text-muted {
font-size: 0.85em;
display: block;
margin-top: 5px;
}
模板文件:base.html (app/templates/base.html)
<!DOCTYPE html>
<html>
<head>
<title>Todo App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div class="container mt-4">
{% block content %}{% endblock %}
</div>
</body>
</html>
index.html (app/templates/index.html)
{% extends "base.html" %}
{% block content %}
<h1 class="mb-4">Todo List</h1>
<form method="POST" class="mb-4">
<div class="mb-3">
<input type="text" class="form-control" name="title" placeholder="Title" required>
</div>
<div class="mb-3">
<textarea class="form-control" name="description" placeholder="Description"></textarea>
</div>
<button type="submit" class="btn btn-primary">Add Todo</button>
</form>
<div class="list-group">
{% for todo in todos %}
<div class="list-group-item {% if todo.completed %}list-group-item-success{% endif %}">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5>{{ todo.title }}</h5>
<p class="mb-0">{{ todo.description }}</p>
<small class="text-muted">{{ todo.created_at.strftime('%Y-%m-%d %H:%M') }}</small>
</div>
<div>
<a href="{{ url_for('main.edit', id=todo.id) }}" class="btn btn-sm btn-warning">Edit</a>
<a href="{{ url_for('main.delete', id=todo.id) }}" class="btn btn-sm btn-danger">Delete</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
运行脚本:run.py
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
以上为全部代码,可以将其复制到对应文件创建。
三、install flask和运行项目
在todo文件目录下打开cmd窗口,激活todoenv环境
conda activate todoenv
刚才requirement.txt已经有安装依赖了,执行命令,安装项目依赖
pip install -r requirements.txt
设置环境变量,我是Windows
set FLASK_APP=run.py
set FLASK_DEBUG=1
初始化数据库
flask db init # 首次运行需要初始化迁移仓库
flask db migrate # 生成迁移脚本
flask db upgrade # 应用迁移到数据库
我这里已经创建过了,所以一次存在不为空了
运行应用
flask run
在本地浏览器打开http://127.0.0.1:5000网址
可以看到,我之前已经运行过这个,数据库在本地也有值了,所以数据库正常运行了,可以新建todo,以及修改删除
Ctrl + C结束程序退出
四、项目结构讲解
项目根目录 todoapp/
todoapp/
├── app/ # 核心应用代码
├── config.py # 全局配置文件
├── requirements.txt # 项目依赖清单
└── run.py # 启动脚本
核心应用模块 app/
文件/目录 | 作用 |
---|---|
__init__.py | 应用工厂函数 • 初始化 Flask 应用实例 • 整合配置/路由/模型等模块 |
models.py | 数据模型 • 定义数据库表结构 (使用 SQLAlchemy 或 Peewee 等 ORM) |
routes.py | 路由控制器 • 处理 HTTP 请求与响应 • 业务逻辑的主要实现位置 |
templates/ | Jinja2 模板 • 存放 HTML 模板文件 • 通过 base.html 实现模板继承 |
static/ | 静态资源 • 存放 CSS/JavaScript/图片等文件 • 通过 /static/路径 访问 |
该项目遵循MVC模式:模型(Model)、视图(View)和控制器(Controller)
模型(Model):负责处理数据和业务逻辑,像数据的存储、检索、更新等操作都由它完成。
app/models.py
:此文件通常定义了应用的数据模型。在一个待办事项应用(todoapp)里,会定义待办事项的类,包含待办事项的属性(如标题、描述、完成状态等)以及对这些数据进行操作的方法(如添加、删除、更新待办事项等)。
视图(View):负责将模型中的数据呈现给用户,通常是用户界面,比如网页、图形界面等。
app/templates/
目录:该目录存放 HTML 模板文件,这些文件用于展示数据给用户。base.html
:通常是基础模板,包含所有页面共有的部分,像头部、导航栏、底部等,其他模板可以继承它。index.html
:一般用于展示待办事项列表,从模型获取待办事项数据并渲染成 HTML 页面。edit.html
:用于编辑单个待办事项的页面,同样从模型获取数据并提供编辑界面。
控制器(Controller):接收用户的输入,调用模型进行相应的业务处理,再选择合适的视图展示处理结果。
app/routes.py
:该文件定义了应用的路由和视图函数,负责接收用户的请求,调用模型进行数据处理,然后选择合适的视图进行渲染
整体交互流程
- 用户通过浏览器访问应用的 URL,请求会发送到
app/routes.py
中的相应路由。 - 路由对应的视图函数被调用,视图函数根据需求调用
app/models.py
中的模型进行数据处理。 - 模型处理完数据后,视图函数选择合适的 HTML 模板(
app/templates/
目录下的文件),并将处理后的数据传递给模板。 - 模板引擎将数据填充到 HTML 模板中,生成最终的 HTML 页面返回给用户浏览器进行显示。
浏览器 → run.py → app/__init__.py (创建app) → routes.py (处理逻辑) → models.py (操作数据库) → templates/*.html (渲染) → 返回响应
浏览器->>run.py: 发起HTTP请求
run.py->>__init__.py: 创建app实例
__init__.py->>routes.py: 注册路由
routes.py->>models.py: 调用数据库操作
models.py->>数据库: 执行SQL
数据库-->>models.py: 返回数据
models.py-->>routes.py: 返回模型对象
routes.py-->>浏览器: 渲染模板响应