Python 虚拟环境 virtualenv & uvicorn 服务搭建 & FAstAPI 使用

虚拟环境

为什么需要虚拟环境?

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

http://127.0.0.1:8000/docs

执行:


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

相关推荐
AI视觉网奇2 小时前
vllm 多卡部署
python
精明的身影2 小时前
网络计划WebApp求解:融合Python与AI决策的项目管理系统
网络·python·web app
AI科技星2 小时前
全域谱分析:无穷维超复数信息场分形统一场论——自然、量子、金融多重分形第一性原理完整体系(中英双语终稿)
人工智能·python·算法·金融·乖乖数学·全域数学
三月不知肉味y2 小时前
2026-07-05-JAVA面试场景题训练
java·开发语言·面试
用户0332126663673 小时前
使用 Python 在 Word 文档中添加批注
python
蜡笔削薪3 小时前
财联万业(杭州)数字科技有限公司能否给代理划定独家经营区域?
大数据·人工智能·python·科技
豆瓣鸡3 小时前
封装 API 请求日志切面——从注解定义到 Starter 自动装配
java·开发语言·spring boot
站大爷IP3 小时前
Python的字典合并坑了我三天,原来是踩了这个坑
python
Tbisnic3 小时前
从链式法则到ReLU:梯度消失的数学本质与工程解决方案
python·深度学习·大模型·激活函数·梯度消失