虚拟环境
为什么需要虚拟环境?
Python项目的一大痛点是依赖管理------项目A需要fastapi==0.95,项目B需要fastapi==0.100,若全部装到系统Python中必然冲突。虚拟环境(Virtual Environment)正是为解决此问题而生:它为每个项目创建独立的Python运行环境,实现依赖隔离。
虚拟环境工具选型
| 工具名称 | 类型 | Python版本支持 | 安装方式 | 特点 | 适用场景 |
|---|---|---|---|---|---|
| venv(推荐) | 内置模块 | ≥ 3.3 | 无需安装,内置 | 轻量级、官方推荐、使用简单 | 通用开发、日常项目 |
| virtualenv | 第三方工具 | 2.x 和 3.x | pip install virtualenv |
功能丰富、兼容多版本 | 需要兼容旧版本或高级功能 |
| conda | Anaconda自带 | 2.x 和 3.x | 随 Anaconda/Miniconda 安装 | 跨语言包管理、数据科学生态 | 数据科学、机器学习项目 |
若需更老版本支持,可使用 virtualenv(Python 2兼容)。下面使用virtualenv来做示例。
虚拟环境创建
安装工具
pip install virtualenv
Collecting virtualenv
Using cached virtualenv-21.5.1-py3-none-any.whl.metadata (3.4 kB)
Collecting distlib<1,>=0.3.7 (from virtualenv)
Using cached distlib-0.4.3-py2.py3-none-any.whl.metadata (5.3 kB)
Collecting filelock<4,>=3.24.2 (from virtualenv)
Using cached filelock-3.29.5-py3-none-any.whl.metadata (2.0 kB)
Collecting platformdirs<5,>=3.9.1 (from virtualenv)
Using cached platformdirs-4.10.0-py3-none-any.whl.metadata (5.5 kB)
Collecting python-discovery>=1.4.2 (from virtualenv)
Using cached python_discovery-1.4.3-py3-none-any.whl.metadata (5.6 kB)
Using cached virtualenv-21.5.1-py3-none-any.whl (4.6 MB)
Using cached distlib-0.4.3-py2.py3-none-any.whl (470 kB)
Using cached filelock-3.29.5-py3-none-any.whl (45 kB)
Using cached platformdirs-4.10.0-py3-none-any.whl (22 kB)
Using cached python_discovery-1.4.3-py3-none-any.whl (33 kB)
Installing collected packages: distlib, platformdirs, filelock, python-discovery, virtualenv
Successfully installed distlib-0.4.3 filelock-3.29.5 platformdirs-4.10.0 python-discovery-1.4.3 virtualenv-21.5.1
创建虚拟环境
python -m virtualenv .venv
标准用法:
virtualenv .venv
.venv 是虚拟环境目录
创建后目录:
.venv/
├── bin/ # 在 Unix/Linux 系统上
│ ├── activate # 激活脚本
│ ├── python # 环境 Python 解释器
│ └── pip # 环境的 pip
├── Scripts/ # 在 Windows 系统上
│ ├── activate # 激活脚本
│ ├── python.exe # 环境 Python 解释器
│ └── pip.exe # 环境的 pip
└── Lib/ # 安装的第三方库

启用虚拟环境
Windows(CMD / PowerShell):
.\.venv\Scripts\activate

包操作
查看命令:pip list

安装:pip install fastapi

查看组件:pip show fastapi
Name: fastapi
Version: 0.139.0
Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production
Home-page: https://github.com/fastapi/fastapi
Author:
Author-email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
License-Expression: MIT
Location: D:\workspace3\python\venv_project\.venv\Lib\site-packages
Requires: annotated-doc, pydantic, starlette, typing-extensions, typing-inspection
Required-by:
已安装的包导出到文件:pip freeze > requirements.txt

新环境安装所有依赖:pip install -r requirements.txt

退出虚拟环境
deactivate
退出后,命令行提示符会恢复正常,Python 和 pip 命令将使用系统全局版本。
删除虚拟环境
虚拟环境本质上是一个普通目录,删除目录即彻底移除:
rmdir /s /q .venv
注意 :删除前请先执行 deactivate 退出环境,否则当前 Shell 会保留失效的环境变量。
Uvicorn
Uvicorn 是一个基于 ASGI(异步服务器网关接口)的 Python Web 服务器,常与 FastAPI 等现代异步框架搭配使用。它的操作主要围绕安装 、运行 和配置展开。
安装
安装基础版本
pip install uvicorn
推荐:安装标准版本(包含 uvloop, httptools 等性能优化组件和开发工具)
pip install 'uvicornstandard'
启动
python -m uvicorn main:app --reload

FAstAPI &SQLAlchemy 示例
main.py
python
# 教程:https://www.runoob.com/fastapi/fastapi-tutorial.html
from fastapi import FastAPI
app = FastAPI()
# GET http://127.0.0.1:8000/
@app.get("/")
async def root():
return {"message": "Hello World"}
# GET http://127.0.0.1:8000/items/11?q1=12
# OUT: {"item_id":11,"q":"12","p2":"p22"}
# q1: str = None 可以没有
# p2:str = "p22" 字符串 默认p22
@app.get("/items/{item_id}")
def read_item(item_id: int, q1: str = None,p2:str = "p22"):
return {"item_id": item_id, "q": q1 ,"p2":p2}
# 路由处理函数返回一个 Pydantic 模型实例,FastAPI 将自动将其转换为 JSON 格式,并作为响应发送给客户端:
from pydantic import BaseModel
class Item(BaseModel):
name:str
age:int = None
# POST http://127.0.0.1:8000/items1
# Body:{
# "name": "123123啊啊啊",
# "age": 1111
# }
@app.post("/items1")
def items1(item:Item):
print("items1:",item.age,item.name)
print(item.model_dump()) # {'name': 'string111', 'age': 123}
print(item.model_dump_json()) # {"name":"string111","age":123}
return item
# 请求头和 Cookie
# 使用 Header 和 Cookie 类型注解获取请求头和 Cookie 数据。
from fastapi import Header, Cookie
# {
# "User-Agent": "PostmanRuntime/7.1.1",
# "Session-Token": null
# }
@app.get("/item2")
def item2(user_agent: str = Header(None), session_token: str = Cookie(None)):
return {"User-Agent": user_agent, "Session-Token": session_token}
# 重定向
# 使用 RedirectResponse 实现重定向,将客户端重定向到 /item2/ 路由。
from fastapi.responses import RedirectResponse
@app.get("/redirect")
def redirect():
return RedirectResponse(url="/item2/")
# 设置状态码
from fastapi import HTTPException
@app.get("/items1/{item_id}")
def read_item(item_id: int):
if item_id == 42:
raise HTTPException(status_code=404, detail="Item not found")
return {"item_id": item_id}
# OUT : 404
# {
# "detail": "Item not found"
# }
# 自定义响应头
from fastapi.responses import JSONResponse
@app.get("/default_header")
def default_header():
content = {"item_id": 1000,"body":10001}
headers = {"X-Custom-Header": "custom-header-value1111111111"}
return JSONResponse(content=content, headers=headers)
# Pydantic 模型支持继承,可以方便地创建输入模型和输出模型:
from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
username: str # 必填
email: EmailStr # 必填,自动校验邮箱格式
full_name: str | None = None # 可选
# 创建用户时的输入模型(包含密码)
class UserCreate(UserBase):
password: str # 必填
# 返回用户信息时的输出模型(不包含密码)
class UserOut(UserBase):
id: int # 由服务器生成
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserCreate):
# 函数接收 UserCreate(含密码),但响应使用 UserOut(不含密码)
# 这样密码就不会出现在 API 响应中
# <bound method BaseModel.model_dump_json of UserCreate(username='string111', email='user@example22.com', full_name='string333', password='string444')>
print(user.model_dump_json)
return {"id": 1, **user.model_dump(exclude={"password"})}
# form
from fastapi import FastAPI, Form
@app.post("/login/")
async def login(
username: str = Form(), # 必填表单字段
password: str = Form(), # 必填表单字段
):
print(f"{password=}")
return {"username": username}
@app.post("/items/")
async def create_item(
name: str = Form(...), # 必填
description: str | None = Form(None), # 可选,默认 None
price: float = Form(..., gt=0), # 必填,必须大于 0
):
return {"name": name, "description": description, "price": price}
# router
from fastapi import APIRouter, Depends, HTTPException, Request
router = APIRouter(
prefix="/posts", # 所有路由自动加上 /posts 前缀
tags=["文章"] # Swagger 文档中的分组标签
)
# 挂载 APIRouter:
app.include_router(router)
@router.get("/{post_id}", response_model=None, name="post_detail")
def post_detail(post_id: int):
"""文章详情 """
return {"post_id":post_id}
# 导入数据库配置
import database,models
# 文件路径:main.py 启动事件中创建表
from fastapi import FastAPI
from database import engine, Base
from models import Category, Post # 确保模型类被导入,Base.metadata 才能识别
@app.on_event("startup")
def on_startup():
"""应用启动时自动创建数据库表(仅开发使用,只要有对应表名称对,那么就不需要重新创建)"""
Base.metadata.create_all(bind=engine)
#执行SQL
# 参考:https://blog.csdn.net/weixin_42743844/article/details/156913924
import seed
@app.post("/insert_data")
async def insert_data():
seed.exec()
return "success..."
database.py
python
from sqlalchemy import create_engine
# MySQL连接字符串格式:mysql+pymysql://用户名:密码@主机:端口/数据库名
# 注意:首次连接需要先在MySQL中创建数据库(比如test_db)
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/pythondb',
echo=True, # 打印执行的SQL(调试用,上线关闭)
pool_size=5, # 连接池大小(默认5)
max_overflow=10 # 超出连接池后的最大连接数(默认10)
)
# 测试连接(无报错则成功)
with engine.connect() as conn:
print("连接成功!")
# =============================
# 创建会话工厂
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建BaseModule类型
from sqlalchemy.orm import declarative_base,DeclarativeBase
Base = declarative_base()
# Base 类:所有 ORM 模型继承自它
class Base1(DeclarativeBase):
pass
# =============================
from fastapi import Depends
def get_db():
"""FastAPI 依赖注入:为每个请求创建独立的数据库会话"""
db = SessionLocal()
try:
yield db # 请求期间使用这个会话
finally:
db.close() # 请求结束后自动关闭会话,防止连接泄漏
models.py
python
# 文件路径:models.py
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from database import Base
class Category(Base):
"""文章分类"""
__tablename__ = "categories"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), unique=True, nullable=False)
slug = Column(String(50), unique=True, nullable=False)
# back_populates 双向关系
posts = relationship("Post", back_populates="category")
class Post(Base):
"""博客文章"""
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(200), nullable=False)
slug = Column(String(200), unique=True, nullable=False)
summary = Column(Text, default="")
content = Column(Text, nullable=False)
category_id = Column(Integer, ForeignKey("categories.id"), nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 与 Category 的双向关系
category = relationship("Category", back_populates="posts")
启动Server
命令:python -m uvicorn main:app --reload
INFO: Will watch for changes in these directories: 'D:\\\\workspace3\\\\python\\\\fastapi'
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process 1780 using WatchFiles
2026-07-06 16:49:18,153 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2026-07-06 16:49:18,153 INFO sqlalchemy.engine.Engine raw sql {}
2026-07-06 16:49:18,154 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2026-07-06 16:49:18,155 INFO sqlalchemy.engine.Engine raw sql {}
2026-07-06 16:49:18,155 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2026-07-06 16:49:18,156 INFO sqlalchemy.engine.Engine raw sql {}
连接成功!
INFO: Started server process 6140
INFO: Waiting for application startup.
2026-07-06 16:49:18,290 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-07-06 16:49:18,290 INFO sqlalchemy.engine.Engine DESCRIBE `pythondb`.`categories`
2026-07-06 16:49:18,290 INFO sqlalchemy.engine.Engine raw sql {}
2026-07-06 16:49:18,292 INFO sqlalchemy.engine.Engine DESCRIBE `pythondb`.`posts`
2026-07-06 16:49:18,292 INFO sqlalchemy.engine.Engine raw sql {}
2026-07-06 16:49:18,294 INFO sqlalchemy.engine.Engine COMMIT
INFO: Application startup complete.
访问 API Doc

执行:



如果这篇文章对你有帮助,欢迎点赞、收藏、转发!有任何疑问或想深入探讨某个框架,评论区见!