资讯动态

Python全栈开发从入门到上线:FastAPI+前端+数据库+部署完整路径

发布时间:2026/10/16 11:14:02 来源:尧图企业网站定制
简介面向希望系统掌握Python全栈开发技能的学生、转行者与初中级工程师教程内容以开发流程为主线覆盖前后端协作、接口调试、异常处理与项目部署等核心场景帮助学习者摆脱碎片化知识建立完整开发视角。包内共282个文件整体仅614KB以230个py源码文件为主配以24个md文档、19个png图片及少量json、html、txt等辅助内容形成轻量而完整的示例库与笔记集便于离线查阅也方便快速定位所需知识点。已有143人下载学习适合自学或小规模培训反复对照亦可在团队内部分享使用。py文件按模块拆分并保留可运行示例md文档写明设计思路与运行说明png图示范关键界面与流程json等配置则还原环境依赖和工程设置可直接调试排查问题。整体目录紧凑、定位清晰既可通读建立全栈框架也可按需抽取模块作参考或二次修改。1. 从“会写脚本”到“能交付系统”Python全栈开发到底在学什么很多人学Python从爬虫或者数据分析入手写200行脚本能跑通一个截图工具但让他做一个带界面、能存数据、别人也能用的系统马上就卡住了。这就是“会Python”和“会Python全栈开发”之间的分界线。所谓Python全栈开发不是要把Python用到所有端而是指用Python撑起后端业务逻辑再配合Web前端、数据库和部署手段完整交付一个可运行的系统。这里讲的不是课程大纲也不是工具列表而是一条能从零跟着做到发布上线的学习路径。你会看到框架怎么选、接口怎么写、页面怎么接、部署怎么弄以及我自己反复踩过的几个坑。适合两类人一类是已经学过Python基础、想独立做产品的个人开发者另一类是从后台脚本转向Web开发需要系统补全知识边界的工程师。2. 学习路线先立骨架四阶段划分与框架选型的理由系统学习最怕的就是东一榔头西一棒子今天学Requests明天学Django后天又去折腾前端框架最后什么都会一点但拼不出一个完整项目。我整理过很多份学习计划最后沉淀下来的是四个阶段基础语法和工程习惯、后端框架与接口设计、数据库与前端联动、部署与测试。这四个阶段不是知识清单而是按“让系统跑起来”这个目标倒推出的依赖关系。2.1 为什么按这四个阶段推进而不是按知识点学按知识点学的问题是不知道学完能干什么。比如列表推导式很重要但单独学完你并不觉得它能帮你做出网站。反过来按阶段学每个阶段结束时你都有一个可运行的小产物。第一阶段产物是一个命令行工具比如一个能读配置文件的批量改名脚本第二阶段产物是一个能提供JSON数据的后端服务用浏览器或Postman能调到第三阶段产物是一个表单能写入数据库的完整页面第四阶段产物是部署到公网服务器上别人通过域名能用。这种递进关系的核心是“依赖前置”。前端页面要调接口所以你得先有接口接口要存数据所以你得先懂数据库基本操作数据库又要被代码连接所以你得知道怎么管理配置和依赖。因此我不推荐先学HTML再学Python也不推荐一上来就学Vue全家桶。先把后端链路走通再补前端踩坑时你能更快定位是前端问题还是后端问题。我自己带过A同学他一开始就学Django和Vue结果前后端同时报错一个月都在调环境。后来按这个四阶段重来第一周就做出了一个本地能用的留言板。可见顺序本身就是学习方法的一部分。2.2 后端框架选型FastAPI、Flask和Django怎么选全栈开发里后端框架是骨架。常见的Python后端框架有Flask、Django和FastAPI我一般会按项目规模和团队习惯来选但作为系统学习路径我推荐先用FastAPI或Flask不推荐一上来就用Django。原因有三个。第一Django自带ORM、Admin、模板系统功能完整但正因为它太完整新手容易“会用了但不知道原理”。比如Django的ORM帮你做了数据库迁移你可能连表结构都不会看出了问题黑匣子一样。第二Flask是最轻量的一个文件就能把服务跑起来路由、请求、响应这些Web核心概念都能看得清清楚楚。坏处是项目一大人就容易把代码堆在一个文件里需要自己学会组织结构。第三FastAPI是近几年比较流行的一个选择它基于类型注解自动生成接口文档性能也不错写起来很顺手而且它的依赖注入机制比Flask稍微高级一点但学习曲线也没有陡到不可接受。我的建议如果你想要最短路径跑通全栈用FastAPI如果你喜欢极简和自由用Flask如果你入职的公司已经用了Django那再单独补Django也不迟。学习阶段不必纠结先把一个框架彻底吃透比三个框架都摸一遍强得多。下面是快速选型表维度FlaskFastAPIDjango上手难度低中低中高自带ORM无无有自动API文档需扩展自带需扩展适合学习非常合适合适偏工程项目组织自由但易乱结构清晰强制规范选好框架之后最重要的就是先跑通一个Hello World。不必追求“项目结构规范”先让一个接口返回JSON你就已经迈过了全栈最关键的“后端能对外说话”这一关。2.3 最小环境搭建虚拟环境与项目骨架命令环境问题经常让刚开始学的人翻车。很多人直接在系统Python里pip install装了一堆包第二天某个包升级项目就挂了。我建议从第一天就养成用虚拟环境的习惯。Python自带的venv就够了不需要额外装Anaconda。在命令行里执行下面的命令创建一个项目目录并激活虚拟环境mkdir myfullstack cd myfullstack python3 -m venv venv source venv/bin/activate pip install --upgrade pip这段命令的意思是第一行新建一个叫myfullstack的文件夹第二行进入它第三行用python3解释器创建一个名为venv的虚拟环境第四行激活这个环境第五行升级pip。注意在Windows环境下第四行要改成venv\Scripts\activate这是最容易踩的第一个环境坑。接着安装FastAPI和UvicornUvicorn是一个ASGI服务器负责启动我们写的FastAPI应用。pip install fastapi uvicorn[standard]安装完成后创建一个名为app.py的文件写入下面的内容from fastapi import FastAPI app FastAPI() app.get(/) def read_root(): return {message: hello fullstack}这个文件定义了一个FastAPI应用并注册了一个根路径的GET接口。函数名read_root没有任何强制要求但建议用能表达意图的名字。注意app:app的含义冒号前是文件名app.py冒号后是文件内创建的应用对象app。--reload表示文件改动后自动重启适合开发环境--port 8000指定监听端口。启动后浏览器访问http://127.0.0.1:8000能看到返回的JSON访问http://127.0.0.1:8000/docs还能看到FastAPI自动生成的接口文档。到这里后端的最小骨架已经立住了。成功启动服务后一定要把当前依赖固化到requirements.txtpip freeze requirements.txt这样换机器部署时一条pip install -r requirements.txt就能重建环境。注意pip freeze会把所有传递依赖都列全如果你不想连无关包也写进去可以先用pip install pip-tools管理精确依赖。这个习惯能让你的项目具备可复现性也是全栈工程化里经常被忽略的细节。3. 从零搭一个前后端分离的最小项目手把手代码与联调顺序第二章我们跑通了一个返回JSON的接口但那只是“后端能说话”。一个真正的全栈最小项目需要包含后端接口、数据存储、前端页面以及把它们串起来的联调。这一章就以“在线待办事项”为例从零写一个能用的前后端分离项目。前后端分离的意思是前端页面通过HTTP请求调用后端接口后端返回JSON前端渲染DOM两边只通过接口通信不共享变量。3.1 定义待办事项的数据模型与数据库初始化我们使用FastAPI作为后端框架SQLite作为数据库。SQLite是Python内置支持的轻量数据库无需单独安装服务端非常适合学习阶段和中小型工具。常见做法是用Python标准库中的sqlite3来操作但为了代码更简洁、减少拼SQL的错误我们引入一个轻量ORM叫SQLAlchemy。SQLAlchemy能让我们用Python对象的方式操作数据库还能避免SQL注入。先安装依赖pip install fastapi uvicorn[standard] sqlalchemy注意这里我们把sqlalchemy加到原有依赖里如果你之前已经装过fastapi和uvicorn只需要单独安装sqlalchemy即可。然后创建database.py文件负责数据库连接和会话管理from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base SQLALCHEMY_DATABASE_URL sqlite:///./todos.db engine create_engine( SQLALCHEMY_DATABASE_URL, connect_args{check_same_thread: False} ) SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) Base declarative_base()逻辑说明create_engine会创建一个数据库引擎sqlite:///./todos.db表示在当前目录下生成名为todos.db的文件。connect_args{check_same_thread: False}是SQLite特有的参数因为SQLite默认只允许创建它的线程访问而FastAPI的多个请求可能在不同线程执行不设置这个参数会报错。sessionmaker负责生成数据库会话autocommitFalse表示必须手动提交事务这是Web应用的常见配置能让你在出错时回滚。declarative_base()是SQLAlchemy 2.0风格的基类后面定义的表模型都要继承它。接着定义数据模型models.pyfrom sqlalchemy import Column, Integer, String, Boolean from database import Base class Todo(Base): __tablename__ todos id Column(Integer, primary_keyTrue, indexTrue) title Column(String, indexTrue) completed Column(Boolean, defaultFalse)这里__tablename__是表名Column定义字段类型indexTrue表示对该字段建立索引适合做查询条件的字段。我们只保留了id、标题和完成状态三个字段足够演示了。还需要初始化建表。在main.py中导入Base和Todo然后调用Base.metadata.create_all(bindengine)from fastapi import FastAPI from database import Base, engine import models Base.metadata.create_all(bindengine) app FastAPI() app.get(/) def read_root(): return {message: todo api is running}create_all会读取所有继承Base的模型在数据库中创建不存在的表。这个调用放在导入模型之后确保模型已经被注册。你可以运行uvicorn main:app --reload启动后目录下会出现todos.db文件。到这里数据存取的基础具备了。3.2 实现增删改查接口用Pydantic校验请求体现在我们来写真正业务用的四个接口创建待办、查询列表、更新状态、删除待办。FastAPI依赖Pydantic来做请求体校验我们新建schema.py定义接口的输入输出格式from pydantic import BaseModel class TodoCreate(BaseModel): title: str completed: bool False class TodoUpdate(BaseModel): title: str | None None completed: bool | None NoneTodoCreate表示创建时需要传titlecompleted可以省略默认False。TodoUpdate把两个字段都设为可选这样前端可以只传需要修改的字段。str | None的写法在Python 3.10及以上有效如果你用的是3.8/3.9需要改成Optional[str]。然后在main.py中增加CRUD接口from fastapi import FastAPI, Depends, HTTPException from sqlalchemy.orm import Session from database import Base, engine, SessionLocal import models import schemas Base.metadata.create_all(bindengine) app FastAPI() def get_db(): db SessionLocal() try: yield db finally: db.close() app.post(/todos, response_modelschemas.TodoCreate) def create_todo(todo: schemas.TodoCreate, db: Session Depends(get_db)): db_todo models.Todo(titletodo.title, completedtodo.completed) db.add(db_todo) db.commit() db.refresh(db_todo) return db_todo app.get(/todos) def list_todos(db: Session Depends(get_db)): return db.query(models.Todo).all() app.put(/todos/{todo_id}, response_modelschemas.TodoUpdate) def update_todo(todo_id: int, todo: schemas.TodoUpdate, db: Session Depends(get_db)): db_todo db.query(models.Todo).filter(models.Todo.id todo_id).first() if not db_todo: raise HTTPException(status_code404, detailtodo not found) if todo.title is not None: db_todo.title todo.title if todo.completed is not None: db_todo.completed todo.completed db.commit() db.refresh(db_todo) return db_todo app.delete(/todos/{todo_id}) def delete_todo(todo_id: int, db: Session Depends(get_db)): db_todo db.query(models.Todo).filter(models.Todo.id todo_id).first() if not db_todo: raise HTTPException(status_code404, detailtodo not found) db.delete(db_todo) db.commit() return {message: deleted}逐段解释get_db是一个依赖函数负责创建和关闭数据库会话。FastAPI会在请求进入时调用它通过yield把会话交给接口函数请求结束后在finally中关闭这个模式能避免连接泄漏。Depends(get_db)告诉FastAPI把get_db生成的值注入到参数db中这是FastAPI依赖注入的标准用法。创建接口app.post(/todos)接收一个符合TodoCreate格式的请求体创建模型对象后加入会话commit提交到数据库refresh从数据库刷新对象以拿到自增id等默认值。更新和删除接口都先查数据库查不到就抛出HTTPException前端会收到404状态码和错误信息。db.query(...).filter(...).first()是SQLAlchemy的查询方式等价于SQL中的SELECT * FROM todos WHERE id? LIMIT 1。启动服务后你可以用FastAPI自带文档页http://127.0.0.1:8000/docs来测试这几个接口。每个接口都有试用的按钮点开就能传参数不用自己写命令行。这是FastAPI比Flask舒服很多的地方。3.3 写一个原生前端页面不引入Node也能调通接口后端接口已经就绪前端我们不用Vue也不用React就用一个简单的index.html文件配合浏览器原生的fetch方法。这样能最大程度减少工具链干扰让你把注意力集中在“浏览器通过HTTP与后端对话”这件事上。创建static/index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 title待办事项/title /head body h1待办事项/h1 input idtitle placeholder输入待办内容 / button idaddBtn添加/button ul idlist/ul script const API_BASE http://127.0.0.1:8000; async function loadTodos() { const resp await fetch(API_BASE /todos); const todos await resp.json(); const listEl document.getElementById(list); listEl.innerHTML ; todos.forEach(todo { const li document.createElement(li); li.textContent todo.title (todo.completed ? [已完成] : ); listEl.appendChild(li); }); } document.getElementById(addBtn).addEventListener(click, async function() { const input document.getElementById(title); const title input.value.trim(); if (!title) return; await fetch(API_BASE /todos, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({title: title}) }); input.value ; loadTodos(); }); loadTodos(); /script /body /html这段代码没有用任何框架。fetch是浏览器内置的HTTP请求方法第一个参数是接口地址第二个参数是请求配置。POST请求必须指定Content-Type为application/jsonbody要手动转成JSON字符串。页面里的loadTodos会在加载时调一次添加待办后再调一次完成数据的刷新。要让FastAPI能服务这个静态文件需要在main.py中配置静态目录。安装aiofiles并加入一段pip install aiofilesfrom fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse app.mount(/static, StaticFiles(directorystatic), namestatic) app.get(/) def home(): return FileResponse(static/index.html)mount把static目录映射到/static路径FileResponse则直接在根路径返回HTML文件。这样浏览器打开http://127.0.0.1:8000/就是前端页面而/todos接口依然在同一个服务下不存在跨域问题。3.4 联调顺序先验证接口再验证页面很多新手一上来就打开页面发现没数据就认为是前端问题或后端问题。我习惯的联调顺序是固定的先启动后端服务确认终端没有报错。访问/docs用文档页直接调用GET /todos能看到空数组[]说明数据库连接正常。用文档页创建一个待办再查一次确认数据写入成功。打开前端页面如果列表能显示刚才手动创建的数据说明前端调用逻辑没问题。最后测试前端添加功能点按钮后列表能自动刷新全链路就通了。如果哪一步没通就往回退一步定位。比如页面请求失败先打开浏览器开发者工具F12看Network标签里那条请求的状态码。状态码404通常是路径不对500通常是后端代码运行时异常跨域报错会显示CORS字样。把问题卡在具体链路层级是联调阶段最重要的技能。4. 系统学习路上的5个常见坑现象、原因与解决办法这一章写的都是我见过的、自己踩过的坑。每个都按“现象、原因、解决”展开你可以把这里当作一本排错手册。这些都是真实高频问题不是理论上的边缘情况。4.1 坑1虚拟环境失效包装到了全局Python现象明明已经source venv/bin/activate但pip list里看不到刚装的fastapi或者启动uvicorn提示找不到模块。更常见的是换了一个终端窗口忘了激活环境然后直接执行命令系统走了全局Python。原因虚拟环境的激活状态不会跨终端保留。新开的终端窗口会重新加载shell配置venv里的Python路径没有被注入所以pip和python都指向全局环境。另一个原因是在某些IDE的终端里自动激活功能没打开需要手动执行激活命令。解决把激活命令记牢每次开新终端都先激活。在项目目录下执行source venv/bin/activate which pythonwhich python会打印当前Python路径如果显示的是/usr/bin/python说明没激活成功如果显示/你的项目路径/venv/bin/python就对了。Windows下使用where python。如果嫌麻烦可以在项目根目录放一个run.sh脚本把激活和启动命令写在一起#!/bin/bash source venv/bin/activate uvicorn main:app --reload这样每次只需执行bash run.sh省去记忆激活步骤。4.2 坑2前端页面打开了但接口请求报CORS跨域错误现象如果你把后端服务跑在8000端口前端页面没有通过FastAPI提供而是直接用浏览器打开了本地的index.html文件file://协议那么前端请求http://127.0.0.1:8000/todos时控制台会报CORS policy相关的错误请求发出去了但读不到响应。原因浏览器出于安全策略默认只允许页面同源请求接口。file://页面和http://127.0.0.1:8000不同源后端没有返回允许跨域的响应头浏览器就把响应拦截了。这是前后端分离项目里最常见的一道坎。解决最简单的方法是像我第3章那样把前端文件交给后端服务托管这样页面和接口同源没有跨域问题。如果一定要严格前后端分离前端部署在另一个域名或端口就需要在后端开启CORS中间件。FastAPI的做法是from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins[*], allow_methods[*], allow_headers[*], )其中allow_origins[*]允许所有来源访问开发阶段可以这么写但生产环境应该改成实际的前端域名比如[https://example.com]否则任何人都能调用你的接口。allow_methods和allow_headers也用*表示允许所有HTTP方法和请求头够用但严格时建议收窄。4.3 坑3SQLite的日期或布尔值读出来“变了样”现象接口返回的字段里布尔值变成了0或1日期变成了字符串前端判断todo.completed true永远不成立。原因SQLite本身存储布尔值时用的是整型0和1SQLAlchemy虽然能把Python的True映射到1但在某些读取场景下特别是使用db.query返回原始行时类型可能不会自动转换回Python布尔类型。更常见的是日期时间字段SQLAlchemy返回datetime对象但如果你自己写了原生SQL查询拿到的就是字符串这会让前端很困惑。解决统一定义Pydantic的响应模型让FastAPI负责序列化。比如在schema.py里为每个接口定义响应模型class TodoOut(BaseModel): id: int title: str completed: bool然后在路由中指定response_modelTodoOutFastAPI会按照模型里的类型声明做转换把0/1转成False/True。对于日期时间可以在Pydantic模型里用datetime类型并配置序列化格式让接口统一返回ISO标准字符串。这个坑的本质是不要相信“数据库里有什么就返回什么”要建立一个明确的API层做类型收口。4.4 坑4前端改了代码但刷新无效或者是缓存现象修改了index.html里的前端逻辑刷新浏览器后还是老样子或者后端接口改了前端请求的还是旧数据。原因这其实有好几个原因。开发模式下浏览器会缓存静态文件还有一个原因是你打开的不是后端托管的页面而是旧的file://文件还有一个原因更隐蔽——你用uvicorn --reload启动时它只监听main.py等被导入的Python文件如果你把index.html放在了static目录但没被Python文件引用--reload不会触发重启但它并不影响静态文件读取所以大概率还是浏览器缓存。解决在浏览器开发者工具Network里勾选“Disable cache”或者强制刷新CtrlShiftR。如果是静态文件被缓存可以在引用时加版本号script标签的src改为/static/main.js?v2后端路由也可以改用带参数的方式。另外养成先看Network响应确认内容是否更新的习惯而不是反复手点刷新。缓存问题看着像玄学本质是HTTP协议里的Cache-Control没配好。4.5 坑5代码全堆在main.py项目一复杂就改不动现象刚开始图省事把路由、数据库、模型、工具函数全部写在main.py一个文件里。功能少的时候还挺顺手等加了认证、多个业务模块后文件上千行改一个地方的函数名要全局搜索替换新加入的同学读起来更痛苦。原因这是Flask和FastAPI新手都会踩的结构性坑。框架本身允许你单文件启动但没有教你项目组织。系统学习的时候如果没提前规划分层代码膨胀是必然的。这不是代码质量“不干净”的问题而是逻辑耦合导致单点修改成本指数上升。解决尽早按职责分层我的习惯是myfullstack/ ├── main.py # 应用入口路由注册 ├── database.py # 数据库引擎与会话 ├── models.py # ORM模型 ├── schemas.py # Pydantic模型 ├── routers/ # 按业务拆分的路由模块 ├── static/ # 前端静态文件 └── tests/ # 测试目录当路由多起来后可以把每个业务模块拆成单独的文件比如routers/todos.py、routers/users.py然后在main.py里用include_router引入。这个坑的规避思路是你在写第3章代码时就养成“一个文件只干一类事”的习惯后面才不会痛苦。5. 把开发机上的项目变成“真系统”认证、部署与自动化测试本地跑通只能算Demo。能对外提供服务的“真系统”至少要解决三个问题怎么识别用户、怎么稳定运行、怎么保证改代码不破坏原有功能。这一章分别对应认证、部署和自动化测试。5.1 用JWT给接口加认证从写死Token到可刷新目前我们的接口谁都能调这在公网上是灾难。常见做法是用JWTJSON Web Token做登录认证。JWT是一种不需要服务端保存会话的Token用户登录后服务端签发一个带过期时间的Token客户端每次请求带上它服务端验签即可。先安装依赖pip install python-jose[cryptography] passlib[bcrypt]python-jose用来签发和验证JWTpasslib和bcrypt用来加密密码。由于密码哈希涉及额外的用户模型这里我们简化处理只演示Token签发和依赖校验密码明文存储只用于教学。在auth.py中写核心逻辑from datetime import datetime, timedelta from jose import jwt, JWTError SECRET_KEY your-secret-key ALGORITHM HS256 ACCESS_TOKEN_EXPIRE_MINUTES 30 def create_access_token(data: dict): to_encode data.copy() expire datetime.utcnow() timedelta(minutesACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({exp: expire}) return jwt.encode(to_encode, SECRET_KEY, algorithmALGORITHM)SECRET_KEY应该保存在环境变量或配置文件中不能写死在代码里否则任何人都能伪造Token。exp是JWT标准过期字段datetime.utcnow()虽然在新版Python中会有弃用提示但在JWT场景仍然可以用。jwt.encode会生成一段由三部分组成的长字符串客户端把它放在请求头的Authorization: Bearer token中。再定义一个依赖函数用来保护需要登录的接口from fastapi import Depends, HTTPException from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials security HTTPBearer() def get_current_user(credentials: HTTPAuthorizationCredentials Depends(security)): token credentials.credentials try: payload jwt.decode(token, SECRET_KEY, algorithms[ALGORITHM]) return payload.get(sub) except JWTError: raise HTTPException(status_code401, detailinvalid token)在需要保护的接口里加入这个依赖app.post(/todos, response_modelschemas.TodoOut) def create_todo( todo: schemas.TodoCreate, db: Session Depends(get_db), user: str Depends(get_current_user) ): db_todo models.Todo(titletodo.title, completedtodo.completed) db.add(db_todo) db.commit() db.refresh(db_todo) return db_todo这样一来没有Token的请求会直接返回401。HTTPBearer会从Authorization头中自动提取Token如果格式不对也会抛错。这一步做完你的接口才算有了一道保护门。5.2 部署到Linux服务器Gunicorn和Nginx反向代理本地开发用的是Uvicorn它是ASGI服务器。生产部署时常见做法是用Gunicorn作为进程管理器配一个Uvicorn worker来跑FastAPI再用Nginx作为反向代理处理静态文件和负载。这里不展开Nginx的完整编译安装只给最小配置。假设你的代码已经在Linux服务器上进入项目目录安装依赖并启动pip install gunicorn gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8000参数说明-w 2表示启动2个worker进程-k uvicorn.workers.UvicornWorker指定worker类型因为FastAPI是ASGI应用不能用默认的同步worker。-b指定监听地址和端口。0.0.0.0表示监听所有网卡的8000端口这样Nginx才能代理到它。然后配置Nginx。在/etc/nginx/sites-available/default或你对应的配置文件中加入server { listen 80; server_name your-domain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static { alias /var/www/myfullstack/static; } }proxy_pass把根路径的请求转发给本地8000端口proxy_set_header把客户端的真实IP和域名传给后端否则后端拿到的所有IP都是Nginx的。location /static直接让Nginx服务静态文件避免后端处理静态资源能减轻压力。修改配置后执行nginx -t systemctl reload nginxnginx -t用来检测语法是否正确reload让新配置生效不需要重启服务。部署阶段有一个血泪经验先直接用curl http://127.0.0.1:8000/todos验证后端服务正常再测试Nginx转发否则你分不清是Nginx问题还是后端问题。5.3 用pytest给核心接口写自动化测试系统能跑起来后怕的是改一处代码把另一处弄坏。自动化测试就是后悔药。我们使用pytest和httpx配合FastAPI的TestClient在内存数据库里跑测试。先安装pip install pytest httpx创建tests/test_todos.pyfrom fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from main import app from database import Base, get_db SQLALCHEMY_DATABASE_URL sqlite:///./test.db engine create_engine(SQLALCHEMY_DATABASE_URL, connect_args{check_same_thread: False}) TestingSessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) def override_get_db(): db TestingSessionLocal() try: yield db finally: db.close() app.dependency_overrides[get_db] override_get_db Base.metadata.create_all(bindengine) client TestClient(app) def test_create_todo(): response client.post(/todos, json{title: 写测试}) assert response.status_code 200 assert response.json()[title] 写测试 def test_list_todos(): client.post(/todos, json{title: 第一条}) response client.get(/todos) assert response.status_code 200 assert len(response.json()) 1这里的关键是app.dependency_overrides[get_db] override_get_db它把接口原本用的get_db依赖替换成测试用的TestingSessionLocal。这样测试时不影响开发数据库todos.db只用独立的test.db。TestClient允许你在不启动网络服务的情况下直接调用应用测试速度很快。运行测试只需要执行pytest tests/ -v如果某个接口的字段类型或者鉴权逻辑写错了测试会立刻红给你看。有了这一层保护后面再做功能扩展、重构代码时心里就不慌了。这一章的三块内容认证、部署、测试都做完你的项目才算有了“系统”的样子。6. 从“能跑”到“能维护”的进阶练习拆模块、上ORM、加日志如果你的目标不是“做完这个教程就停”而是真正把Python全栈当成职业能力那最后这三个练习建议你认真做一遍。它们能让你的代码更抗折腾也让别人能接手。6.1 练习一把路由拆成蓝图Blueprints在FastAPI里我习惯把main.py中所有app.get这类装饰器改到独立的routers目录下。比如把todos相关的四个接口挪到routers/todos.py然后定义from fastapi import APIRouter router APIRouter(prefix/todos, tags[todos])在main.py中用app.include_router(router)引入。这么做最大的收益是每个业务模块都能独立维护还能把同组接口的公共依赖集中处理。你会在拆分过程中发现哪些逻辑是真正通用的哪些只是碰巧长得像。6.2 练习二用ORM迁移代替手动建表前面我们用create_all建表但生产环境里改表结构比如新加一个字段create_all并不会更新已有表。业界常用做法是用Alembic管理数据库迁移。你只需要把模型变化写成迁移脚本然后执行alembic upgrade head数据库结构就跟着代码版本走。这个练习能帮你避开“测试环境好用生产环境忘记改表”的经典事故。6.3 练习三加上结构化日志和错误追踪开发时打印print足够但生产环境几乎没有人在终端盯着。给系统加上logging模块把请求地址、用户、耗时、错误堆栈输出到文件或日志平台是排查线上问题的唯一抓手。不要等到用户报告了才去还原现场日志就是你的事故现场。写完日志再回头看之前写过的接口你会重新理解“可观测性”这几个字。最后说一个我自己的习惯学全栈的过程中我每做完一个阶段就强迫自己写一篇像这样的笔记。写笔记不是给别人看而是逼自己把模糊的想法变成精确的步骤。很多时候你以为自己懂了真写下来才发现某个环节是黑匣子然后就会倒回去把它拆开。这个习惯陪我走过了从脚本到系统交付的全过程。希望这篇教程能让你少踩几个坑也希望帮到你。就这样去跑通你自己的第一个全栈项目吧。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑