Flask 快速入门指南

柳三千

什么是 Flask?

Flask 是一个轻量级的 Python Web 框架,被称为 “微框架”,因为它核心简单但可扩展性强。Flask 提供了构建 Web 应用所需的基本工具,同时允许开发者自由选择数据库、模板引擎等其他组件。

Flask 核心特点:

  • 轻量级:核心简单,易于学习和使用
  • 灵活性:不强制项目结构,可按需扩展
  • Jinja2 模板引擎:强大的模板系统
  • 内置开发服务器:快速测试和调试
  • RESTful 请求处理:优雅处理 HTTP 请求
  • WSGI 兼容:可部署在各种生产服务器

安装 Flask

# 创建虚拟环境(推荐)
python -m venv venv

# 激活虚拟环境
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate

# 安装 Flask
pip install flask

第一个 Flask 应用

创建 app.py 文件:

from flask import Flask

# 创建 Flask 应用实例
app = Flask(__name__)

# 定义路由和视图函数
@app.route('/')
def home():
    return "欢迎来到 Flask 世界!"

@app.route('/hello/<name>')
def hello(name):
    return f"你好, {name}!"

# 运行应用
if __name__ == '__main__':
    app.run(debug=True)

运行应用:

python app.py

访问:

核心概念

1. 路由系统

@app.route('/')
def index():
    return '首页'

@app.route('/user/<username>')
def show_user(username):
    return f'用户: {username}'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'文章 #{post_id}'

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    return f'子路径: {subpath}'

路由变量类型:

  • string: 默认类型,接受任何不包含斜杠的文本
  • int: 接受正整数
  • float: 接受正浮点数
  • path: 类似 string,但接受斜杠
  • uuid: 接受 UUID 字符串

2. 请求处理

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 获取表单数据
        username = request.form['username']
        password = request.form['password']
        
        # 处理登录逻辑
        return f'用户 {username} 已登录'
    else:
        # 显示登录表单
        return '''
            <form method="post">
                用户名: <input type="text" name="username"><br>
                密码: <input type="password" name="password"><br>
                <input type="submit" value="登录">
            </form>
        '''

常用请求对象属性:

  • request.method: HTTP 方法(GET, POST等)
  • request.form: 表单数据
  • request.args: URL 查询参数
  • request.files: 上传的文件
  • request.cookies: cookies
  • request.headers: 请求头
  • request.json: JSON 数据(Content-Type为application/json时)

3. 响应处理

from flask import make_response, redirect, url_for, jsonify

@app.route('/custom-response')
def custom_response():
    # 创建自定义响应
    response = make_response("自定义响应", 201)
    response.headers['X-Custom-Header'] = 'Flask'
    return response

@app.route('/redirect')
def redirect_example():
    # 重定向到其他路由
    return redirect(url_for('home'))

@app.route('/json')
def json_example():
    # 返回JSON数据
    return jsonify({'name': 'Alice', 'email': 'alice@example.com'})

@app.route('/set-cookie')
def set_cookie():
    response = make_response("Cookie已设置")
    response.set_cookie('theme', 'dark', max_age=60*60*24)  # 24小时
    return response

4. 模板渲染(Jinja2)

创建 templates 目录,添加 index.html

<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h1>欢迎, {{ user.username }}!</h1>
    
    {% if user.is_admin %}
        <p>您有管理员权限</p>
    {% else %}
        <p>您是普通用户</p>
    {% endif %}
    
    <ul>
        {% for item in items %}
            <li>{{ item }}</li>
        {% endfor %}
    </ul>
</body>
</html>

在 Flask 中使用模板:

from flask import render_template

@app.route('/dashboard')
def dashboard():
    user = {'username': '张三', 'is_admin': True}
    items = ['苹果', '香蕉', '橙子']
    return render_template('index.html', 
                          title='控制面板',
                          user=user, 
                          items=items)

5. 静态文件

创建 static 目录,存放 CSS、JS 和图片文件。

<!-- 在模板中引用静态文件 -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script src="{{ url_for('static', filename='app.js') }}"></script>
<img src="{{ url_for('static', filename='logo.png') }}">

6. 错误处理

@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    return render_template('500.html'), 500

创建 templates/404.html

<!DOCTYPE html>
<html>
<head>
    <title>页面不存在</title>
</head>
<body>
    <h1>404 - 页面未找到</h1>
    <p>您访问的页面不存在,请检查URL是否正确</p>
    <a href="{{ url_for('home') }}">返回首页</a>
</body>
</html>

数据库集成(SQLite + SQLAlchemy)

安装依赖

pip install flask-sqlalchemy

配置数据库

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)

# 定义数据模型
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    
    def __repr__(self):
        return f"User('{self.username}', '{self.email}')"

# 创建数据库
with app.app_context():
    db.create_all()

数据库操作

@app.route('/add-user')
def add_user():
    # 创建新用户
    new_user = User(username='李四', email='lisi@example.com')
    db.session.add(new_user)
    db.session.commit()
    return '用户已添加'

@app.route('/users')
def list_users():
    # 查询所有用户
    users = User.query.all()
    return render_template('users.html', users=users)

@app.route('/user/<int:user_id>')
def show_user(user_id):
    # 查询单个用户
    user = User.query.get_or_404(user_id)
    return render_template('user.html', user=user)

@app.route('/delete-user/<int:user_id>')
def delete_user(user_id):
    # 删除用户
    user = User.query.get_or_404(user_id)
    db.session.delete(user)
    db.session.commit()
    return redirect(url_for('list_users'))

表单处理

安装依赖

pip install flask-wtf

创建表单类

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email

class RegistrationForm(FlaskForm):
    username = StringField('用户名', 
                          validators=[DataRequired(), Length(min=2, max=20)])
    email = StringField('邮箱', 
                       validators=[DataRequired(), Email()])
    password = PasswordField('密码', 
                            validators=[DataRequired()])
    submit = SubmitField('注册')

在视图函数中使用表单

from flask import flash

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        # 创建新用户
        user = User(username=form.username.data, email=form.email.data)
        db.session.add(user)
        db.session.commit()
        flash('账号创建成功!', 'success')
        return redirect(url_for('home'))
    return render_template('register.html', title='注册', form=form)

注册模板 (register.html)

{% extends "layout.html" %}

{% block content %}
    <h1>注册账号</h1>
    <form method="POST" action="">
        {{ form.hidden_tag() }}
        <div>
            {{ form.username.label }}
            {{ form.username(class="form-control") }}
            {% if form.username.errors %}
                <div class="error">
                    {% for error in form.username.errors %}
                        <span>{{ error }}</span>
                    {% endfor %}
                </div>
            {% endif %}
        </div>
        <div>
            {{ form.email.label }}
            {{ form.email(class="form-control") }}
            {% if form.email.errors %}
                <div class="error">
                    {% for error in form.email.errors %}
                        <span>{{ error }}</span>
                    {% endfor %}
                </div>
            {% endif %}
        </div>
        <div>
            {{ form.password.label }}
            {{ form.password(class="form-control") }}
        </div>
        <div>
            {{ form.submit(class="btn") }}
        </div>
    </form>
{% endblock %}

用户会话管理

from flask import session, redirect, url_for

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first()
        if user and user.password == form.password.data:
            # 设置会话
            session['user_id'] = user.id
            flash('登录成功!', 'success')
            return redirect(url_for('dashboard'))
        else:
            flash('登录失败,请检查邮箱和密码', 'danger')
    return render_template('login.html', form=form)

@app.route('/dashboard')
def dashboard():
    if 'user_id' not in session:
        return redirect(url_for('login'))
    user = User.query.get(session['user_id'])
    return render_template('dashboard.html', user=user)

@app.route('/logout')
def logout():
    # 清除会话
    session.pop('user_id', None)
    flash('您已成功登出', 'info')
    return redirect(url_for('home'))

项目结构

典型的 Flask 项目结构:

my_flask_app/
├── venv/                   # 虚拟环境
├── app/
│   ├── templates/          # HTML模板
│   │   ├── layout.html
│   │   ├── index.html
│   │   ├── register.html
│   │   └── ...
│   ├── static/             # 静态文件
│   │   ├── css/
│   │   ├── js/
│   │   └── images/
│   ├── models.py           # 数据模型
│   ├── forms.py            # 表单类
│   ├── routes.py           # 路由和视图函数
│   └── __init__.py         # 应用工厂
├── migrations/             # 数据库迁移(如果使用Flask-Migrate)
├── config.py               # 配置文件
├── requirements.txt        # 依赖列表
└── run.py                  # 启动脚本

完整示例:待办事项应用

文件结构

todo_app/
├── app/
│   ├── __init__.py
│   ├── models.py
│   ├── routes.py
│   ├── templates/
│   │   ├── base.html
│   │   ├── index.html
│   │   └── edit_task.html
│   └── static/
│       └── style.css
├── config.py
└── run.py

代码实现

config.py

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key'
    SQLALCHEMY_DATABASE_URI = 'sqlite:///todos.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

app/init.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)
    
    db.init_app(app)
    
    from app import routes
    app.register_blueprint(routes.bp)
    
    return app

app/models.py

from app import db

class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text)
    completed = db.Column(db.Boolean, default=False)
    
    def __repr__(self):
        return f'<Task {self.title}>'

app/routes.py

from flask import Blueprint, render_template, redirect, url_for, request, flash
from app import db
from app.models import Task

bp = Blueprint('main', __name__)

@bp.route('/')
def index():
    tasks = Task.query.all()
    return render_template('index.html', tasks=tasks)

@bp.route('/add', methods=['POST'])
def add_task():
    title = request.form['title']
    description = request.form.get('description', '')
    
    if not title:
        flash('任务标题不能为空', 'danger')
        return redirect(url_for('main.index'))
    
    new_task = Task(title=title, description=description)
    db.session.add(new_task)
    db.session.commit()
    flash('任务已添加', 'success')
    return redirect(url_for('main.index'))

@bp.route('/complete/<int:task_id>')
def complete_task(task_id):
    task = Task.query.get_or_404(task_id)
    task.completed = not task.completed
    db.session.commit()
    flash('任务状态已更新', 'info')
    return redirect(url_for('main.index'))

@bp.route('/edit/<int:task_id>', methods=['GET', 'POST'])
def edit_task(task_id):
    task = Task.query.get_or_404(task_id)
    
    if request.method == 'POST':
        task.title = request.form['title']
        task.description = request.form['description']
        db.session.commit()
        flash('任务已更新', 'success')
        return redirect(url_for('main.index'))
    
    return render_template('edit_task.html', task=task)

@bp.route('/delete/<int:task_id>')
def delete_task(task_id):
    task = Task.query.get_or_404(task_id)
    db.session.delete(task)
    db.session.commit()
    flash('任务已删除', 'warning')
    return redirect(url_for('main.index'))

templates/base.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>待办事项</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="container">
        <header>
            <h1>待办事项清单</h1>
        </header>
        
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                <div class="flash-messages">
                    {% for category, message in messages %}
                        <div class="flash {{ category }}">{{ message }}</div>
                    {% endfor %}
                </div>
            {% endif %}
        {% endwith %}
        
        {% block content %}{% endblock %}
    </div>
</body>
</html>

templates/index.html

```html
{% extends “base.html” %}

{% block content %}



文章版权声明:除非注明,否则均为柳三千运维录原创文章,转载或复制请以超链接形式并注明出处。

取消
微信二维码
微信二维码
支付宝二维码