Flask vs FastAPI

Flask vs FastAPI:Python 两大主流框架项目架构详解

拆解 Flask 与 FastAPI 的项目目录结构、核心逻辑代码,并对比两者的架构差异与选型建议

目录


一、前言:为什么是这两个框架

Flask 适合灵活的传统 Web 应用,FastAPI 则是现代 API / 微服务 / AI 工程服务的首选。


二、Flask 项目架构详解

2.1 架构设计理念

Flask 是微框架,核心只提供路由、模板等基本功能,项目结构需要自己设计。典型设计流程是:

数据库设计 → 架构设计 → 代码实现

2.2 推荐项目结构

flasky/
├── app/                        # Flask 程序主目录
│   ├── __init__.py             # 应用工厂 create_app()
│   ├── models.py               # 数据模型
│   ├── email.py                # 异步邮件服务
│   ├── main/                   # 蓝图
│   │   ├── __init__.py         # 创建蓝图对象
│   │   ├── views.py            # 视图函数(路由)
│   │   ├── errors.py           # 错误处理
│   │   └── forms.py            # 表单定义
│   ├── static/                 # 静态文件
│   └── templates/              # HTML 模板
├── settings/                   # 配置目录
│   ├── dev.py                  # 开发环境配置
│   └── prop.py                 # 生产环境配置
├── filter/
│   └── requestFilter.py        # 全局路由拦截器
├── migrations/                 # 数据库迁移脚本
├── tests/                      # 单元测试
├── config.py                   # 基础配置
├── manage.py                   # 启动入口
└── requirements.txt

2.3 核心逻辑代码

① 应用工厂(app/__init__.py)

创建 Flask 应用并加载配置(MySQL、Redis 等):

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app.main import main_bp

db = SQLAlchemy()

def create_app(config_name='dev'):
    app = Flask(__name__)

    # 加载配置
    if config_name == 'dev':
        from settings.dev import Config
    else:
        from settings.prop import Config

    app.config.from_object(Config)

    # 初始化扩展
    db.init_app(app)

    # 注册蓝图(蓝图中的路由此时才真正生效)
    app.register_blueprint(main_bp)

    # 注册全局请求拦截器
    from filter.requestFilter import register_filter
    register_filter(app)

    return app

② 蓝图(app/main/__init__.py)

蓝图中定义的路由处于“休眠”状态,直到注册到程序上才生效:

from flask import Blueprint

main_bp = Blueprint('main', __name__)

from app.main import views, errors  # 避免循环导入,必须放最后

③ 数据模型(app/models.py)

from app import db
from datetime import datetime

class Article(db.Model):
    __tablename__ = 'articles'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(128), nullable=False)
    content = db.Column(db.Text)
    category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    category = db.relationship('Category', backref='articles')

class Category(db.Model):
    __tablename__ = 'categories'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

④ 视图函数(app/main/views.py)

from flask import render_template, request, jsonify
from app import db
from app.main import main_bp
from app.models import Article, Category
from app.main.forms import ArticleForm

@main_bp.route('/')
def index():
    articles = Article.query.order_by(Article.created_at.desc()).all()
    return render_template('index.html', articles=articles)

@main_bp.route('/api/articles', methods=['POST'])
def create_article():
    form = ArticleForm()
    if form.validate_on_submit():
        article = Article(
            title=form.title.data,
            content=form.content.data
        )
        db.session.add(article)
        db.session.commit()
        return jsonify({'code': 0, 'msg': 'success', 'data': {'id': article.id}})
    return jsonify({'code': 1, 'msg': '参数校验失败'})

⑤ 全局拦截器(filter/requestFilter.py)

from flask import request, jsonify

def register_filter(app):
    @app.before_request
    def log_request():
        print(f"[{request.method}] {request.path} from {request.remote_addr}")

⑥ 启动入口(manage.py)

from app import create_app, db

app = create_app('dev')

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(host='0.0.0.0', port=5000, debug=True)

三、FastAPI 项目架构详解

3.1 架构设计理念

FastAPI 基于标准 Python 类型提示构建,底层由 Starlette(Web 部分)+ Pydantic(数据部分)驱动,核心特点:

  • 高性能:可与 NodeJS 和 Go 媲美
  • 开发效率:提升约 200%~300%
  • 减少 Bug:减少约 40% 人为错误
  • 自动文档:部署后访问 /docs(Swagger UI)或 /redoc 即可看到交互式文档

3.2 推荐项目结构

fastapi_project/
├── app/
│   ├── main.py                # 应用入口
│   ├── core/
│   │   ├── config.py          # Pydantic Settings 配置
│   │   └── database.py        # 异步数据库连接
│   ├── models/                # SQLAlchemy ORM 模型
│   │   └── article.py
│   ├── schemas/               # Pydantic 请求/响应模型
│   │   └── article.py
│   ├── crud/                  # 数据库 CRUD 操作层
│   │   └── article.py
│   ├── api/
│   │   └── v1/
│   │       ├── api.py         # 路由汇总
│   │       └── endpoints/
│   │           └── article.py
│   └── dependencies.py        # 依赖注入
├── tests/
└── requirements.txt

3.3 核心逻辑代码

① 配置(app/core/config.py)

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    APP_NAME: str = "MyAPI"
    DATABASE_URL: str = "sqlite+aiosqlite:///./app.db"
    DEBUG: bool = True

    class Config:
        env_file = ".env"

settings = Settings()

② ORM 模型(app/models/article.py)

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.orm import relationship, DeclarativeBase
from datetime import datetime

class Base(DeclarativeBase):
    pass

class Article(Base):
    __tablename__ = "articles"
    id = Column(Integer, primary_key=True)
    title = Column(String(128), nullable=False)
    content = Column(Text)
    category_id = Column(Integer, ForeignKey("categories.id"))
    created_at = Column(DateTime, default=datetime.utcnow)

③ Pydantic 模型(app/schemas/article.py)

这是 FastAPI 解决“字段随便传”痛点的关键:

from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional

# 请求模型:自动校验参数
class ArticleCreate(BaseModel):
    title: str = Field(min_length=3, max_length=128)
    content: Optional[str] = None
    category_id: int = Field(gt=0)

# 响应模型:精确控制输出字段
class ArticleResponse(BaseModel):
    id: int
    title: str
    content: Optional[str]
    created_at: datetime

    class Config:
        from_attributes = True  # 支持从 ORM 对象转换

④ CRUD 层(app/crud/article.py)

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.models.article import Article
from app.schemas.article import ArticleCreate

async def create_article(db: AsyncSession, data: ArticleCreate) -> Article:
    article = Article(**data.model_dump())
    db.add(article)
    await db.commit()
    await db.refresh(article)
    return article

async def get_articles(db: AsyncSession, skip: int = 0, limit: int = 20):
    result = await db.execute(select(Article).offset(skip).limit(limit))
    return result.scalars().all()

async def get_article(db: AsyncSession, article_id: int):
    result = await db.execute(select(Article).where(Article.id == article_id))
    return result.scalar_one_or_none()

⑤ 端点路由(app/api/v1/endpoints/article.py)

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.crud import article as crud
from app.schemas.article import ArticleCreate, ArticleResponse

router = APIRouter(prefix="/articles", tags=["文章管理"])

@router.post("", response_model=ArticleResponse, status_code=status.HTTP_201_CREATED)
async def create_article(data: ArticleCreate, db: AsyncSession = Depends(get_db)):
    """创建文章(参数自动校验,类型不合法直接返回 422)"""
    return await crud.create_article(db, data)

@router.get("", response_model=list[ArticleResponse])
async def list_articles(skip: int = 0, limit: int = 20, db: AsyncSession = Depends(get_db)):
    return await crud.get_articles(db, skip=skip, limit=limit)

@router.get("/{article_id}", response_model=ArticleResponse)
async def get_article(article_id: int, db: AsyncSession = Depends(get_db)):
    article = await crud.get_article(db, article_id)
    if not article:
        raise HTTPException(status_code=404, detail="文章不存在")
    return article

⑥ 应用入口(app/main.py)

from fastapi import FastAPI
from app.api.v1.api import api_router
from app.core.database import engine
from app.models.article import Base

app = FastAPI(
    title="Article API",
    description="文章管理系统",
    version="1.0.0",
)

app.include_router(api_router, prefix="/api/v1")

@app.on_event("startup")
async def startup():
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

运行方式:

uvicorn app.main:app --reload

启动后访问 http://127.0.0.1:8000/docs 即可看到自动生成的交互式 API 文档。


四、核心差异对比

维度 Flask FastAPI
架构风格 微框架,需要自己组装(像搭乐高) 现代分层架构,类型驱动
参数校验 手动校验,或依赖 flask-wtf 表单 Pydantic 自动校验,类型即文档
异步支持 原生较弱,需 async 扩展 原生 async/await,高并发强
API 文档 手写 Swagger,需额外扩展 自动生成 Swagger UI + ReDoc
代码组织 蓝图 + 应用工厂 Router + 依赖注入 + 分层
典型场景 传统全栈网站、模板渲染、小型项目 纯 API、微服务、AI 工程周边服务

五、选型建议

  • 项目是前后端不分离的网站(如个人、后台管理系统),需要模板渲染、Session 认证 → 选 Flask
  • 项目是前后端分离的纯 API 服务、微服务、对接大模型/AI 应用 → 选 FastAPI,能显著减少“字段随便传、异常随便抛”导致的线上脏数据问题

六、总结

两者并非替代关系:Flask 依然适合快速验证想法和灵活组装,而 FastAPI 代表了新项目在 API 时代的心智转变。


参考链接

posted @ 2026-10-01 22:57  -鹿-  阅读(3)  评论(0)    收藏  举报