当前位置: 首页 > article >正文

FastAPI + GraphQL + SQLAlchemy 实现博客系统

本文将详细介绍如何使用 FastAPI、GraphQL(Strawberry)和 SQLAlchemy 实现一个带有认证功能的博客系统。
在这里插入图片描述

技术栈

  • FastAPI:高性能的 Python Web 框架
  • Strawberry:Python GraphQL 库
  • SQLAlchemy:Python ORM 框架
  • JWT:用于用户认证

系统架构

1. 数据模型(Models)

使用 SQLAlchemy 定义数据模型,以用户模型为例:

class UserModel(Base):"""SQLAlchemy model for the users table"""__tablename__ = "users"id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)username: Mapped[str] = mapped_column(String(50), unique=True, index=True)email: Mapped[str] = mapped_column(String(100), unique=True, index=True)hashed_password: Mapped[str] = mapped_column(String(200))nickname: Mapped[str] = mapped_column(String(50), nullable=True)is_active: Mapped[bool] = mapped_column(Boolean, default=True)is_admin: Mapped[bool] = mapped_column(Boolean, default=False)created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)# 关联关系posts = relationship("PostModel", back_populates="author")def verify_password(self, password: str) -> bool:"""验证密码"""return pwd_context.verify(password, self.hashed_password)

2. GraphQL Schema

使用 Strawberry 定义 GraphQL schema,包括查询和变更:

from models.types import (UserRead,      # 用户信息读取类型UserCreate,    # 用户创建输入类型LoginInput,    # 登录输入类型LoginResponse, # 登录响应类型RegisterResponse, # 注册响应类型Token,        # Token类型PostRead,     # 文章读取类型PostCreate,   # 文章创建输入类型PageInput,    # 分页输入类型Page,         # 分页响应类型PageInfo      # 分页信息类型
)@strawberry.type
class Query:@strawberry.fielddef hello(self) -> str:"""测试接口"""return "Hello World"@strawberry.fielddef me(self, info) -> Optional[UserRead]:"""获取当前用户信息- 需要认证- 返回 None 表示未登录- 返回 UserRead 类型表示当前登录用户信息"""if not info.context.get("user"):return Nonereturn info.context["user"].to_read()@strawberry.fielddef my_posts(self, info, page_input: Optional[PageInput] = None) -> Page[PostRead]:"""获取当前用户的文章列表- 需要认证- 支持分页查询- 返回带分页信息的文章列表参数:- page_input: 可选的分页参数- page: 页码(默认1)- size: 每页大小(默认10)返回:- items: 文章列表- page_info: 分页信息- total: 总记录数- page: 当前页码- size: 每页大小- has_next: 是否有下一页- has_prev: 是否有上一页"""# 认证检查if not info.context.get("user"):raise ValueError("Not authenticated")# 数据库操作db = SessionLocal()try:# 设置分页参数page = page_input.page if page_input else 1size = page_input.size if page_input else 10# 查询总数total = db.query(func.count(PostModel.id)).filter(PostModel.author_id == info.context["user"].id).scalar()# 查询分页数据posts = (db.query(PostModel).options(joinedload(PostModel.author))  # 预加载作者信息.filter(PostModel.author_id == info.context["user"].id).order_by(PostModel.created_at.desc())  # 按创建时间倒序.offset((page - 1) * size).limit(size).all())# 构建分页信息page_info = PageInfo(total=total,page=page,size=size,has_next=total > page * size,has_prev=page > 1)return Page(items=[post.to_read() for post in posts],page_info=page_info)finally:db.close()@strawberry.fielddef user_posts(self, username: str, page_input: Optional[PageInput] = None) -> Page[PostRead]:"""获取指定用户的文章列表- 公开接口,无需认证- 支持分页查询- 返回带分页信息的文章列表参数:- username: 用户名- page_input: 可选的分页参数"""# ... 实现类似 my_posts@strawberry.type
class Mutation:@strawberry.mutationdef login(self, login_data: LoginInput) -> LoginResponse:"""用户登录- 公开接口,无需认证- 验证用户名密码- 生成访问令牌参数:- login_data:- username: 用户名- password: 密码返回:- token: 访问令牌- user: 用户信息"""db = SessionLocal()try:# 查找用户user = db.query(UserModel).filter(UserModel.username == login_data.username).first()# 验证密码if not user or not user.verify_password(login_data.password):raise ValueError("Incorrect username or password")# 生成访问令牌access_token = create_access_token(data={"sub": str(user.id)})token = Token(access_token=access_token)return LoginResponse(token=token, user=user.to_read())finally:db.close()@strawberry.mutationdef register(self, user_data: UserCreate) -> RegisterResponse:"""用户注册- 公开接口,无需认证- 检查用户名和邮箱是否已存在- 创建新用户- 生成访问令牌参数:- user_data:- username: 用户名- password: 密码- email: 邮箱返回:- token: 访问令牌- user: 用户信息"""# ... 实现代码@strawberry.mutationdef create_post(self, post_data: PostCreate, info) -> PostRead:"""创建文章- 需要认证- 创建新文章- 设置当前用户为作者参数:- post_data:- title: 标题- content: 内容返回:- 创建的文章信息"""# ... 实现代码schema = strawberry.Schema(query=Query, mutation=Mutation)

认证实现

1. JWT Token 生成

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:"""创建访问令牌"""to_encode = data.copy()if expires_delta:expire = datetime.utcnow() + expires_deltaelse:expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)to_encode.update({"exp": expire})encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)return encoded_jwt

2. 认证中间件

在 FastAPI 应用中实现认证中间件,用于解析和验证 token:

async def get_context(request: Request):"""GraphQL 上下文处理器,用于认证"""auth_header = request.headers.get("Authorization")context = {"user": None}if auth_header and auth_header.startswith("Bearer "):token = auth_header.split(" ")[1]token_data = verify_token(token)if token_data:db = SessionLocal()try:user = db.query(UserModel).filter(UserModel.id == int(token_data["sub"])).first()if user:context["user"] = userfinally:db.close()return context

3. 认证流程

  1. 用户登录:
mutation Login {login(loginData: {username: "admin",password: "111111"}) {token {accessToken}user {idusernameemail}}
}
  1. 服务器验证用户名密码,生成 JWT token

  2. 后续请求中使用 token:

    • 在请求头中添加:Authorization: Bearer your_token
    • 中间件解析 token 并验证
    • 将用户信息添加到 GraphQL context
  3. 在需要认证的操作中检查用户:

if not info.context.get("user"):raise ValueError("Not authenticated")

API 权限设计

1. 公开接口(无需认证)

  • hello: 测试接口
  • login: 用户登录
  • register: 用户注册
  • userPosts: 获取指定用户的文章列表

2. 私有接口(需要认证)

  • me: 获取当前用户信息
  • myPosts: 获取当前用户的文章列表
  • createPost: 创建新文章

使用示例

1. 登录获取 Token

mutation Login {login(loginData: {username: "admin",password: "111111"}) {token {accessToken}}
}

2. 使用 Token 访问私有接口

在 GraphQL Playground 中设置 HTTP Headers:

{"Authorization": "Bearer your_token"
}

然后可以查询私有数据:

query MyPosts {myPosts(pageInput: {page: 1,size: 10}) {items {idtitlecontent}}
}

安全考虑

  1. 密码安全

    • 使用 bcrypt 进行密码哈希
    • 从不存储明文密码
  2. Token 安全

    • 使用 JWT 标准
    • 设置合理的过期时间
    • 使用安全的签名算法
  3. 数据访问控制

    • 严格的权限检查
    • 用户只能访问自己的数据

总结

本项目展示了如何使用现代化的技术栈构建一个安全的 GraphQL API:

  1. 使用 FastAPI 提供高性能的 Web 服务
  2. 使用 Strawberry 实现 GraphQL API
  3. 使用 SQLAlchemy 进行数据库操作
  4. 实现了完整的认证机制
  5. 遵循了最佳安全实践

当然图片上传一类的,还要跟以前一样写,但现在我们只写了一个/api接口就完成了项目所有接口。

相关文章:

FastAPI + GraphQL + SQLAlchemy 实现博客系统

本文将详细介绍如何使用 FastAPI、GraphQL(Strawberry)和 SQLAlchemy 实现一个带有认证功能的博客系统。 技术栈 FastAPI:高性能的 Python Web 框架Strawberry:Python GraphQL 库SQLAlchemy:Python ORM 框架JWT&…...

React第二十八章(css modules)

css modules 什么是 css modules 因为 React 没有Vue的Scoped,但是React又是SPA(单页面应用),所以需要一种方式来解决css的样式冲突问题,也就是把每个组件的样式做成单独的作用域,实现样式隔离,而css modules就是一种…...

昆仑万维Java开发面试题及参考答案

进程和线程的区别是什么? 进程和线程都是操作系统中非常重要的概念,它们在多个方面存在显著的区别。 从定义上看,进程是操作系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间,包括代码段、数据段、堆栈段等。例如,当你在电脑上同时打开浏览器和音乐播放…...

DeepSeek R1-Zero vs. R1:强化学习推理的技术突破与应用前景

📌 引言:AI 推理的新时代 近年来,大语言模型(LLM) 的规模化扩展成为 AI 研究的主流方向。然而,LLM 的扩展是否真的能推动 通用人工智能(AGI) 的实现?DeepSeek 推出的 R1…...

Linux《基础指令》

在之前的Linux《Linux简介与环境的搭建》当中我们已经初步了解了Linux的由来和如何搭建Linux环境,那么接下来在本篇当中我们就要来学习Linux的基础指令。在此我们的学习是包括两个部分,即指令和关于Linux的基础知识;因此本篇指令和基础知识的…...

2024.12.28测试 总结

还是超级无敌寀啊~ 目录 T1 赠送笔记本T2 中位数T3 好子集T4 异或总结 T1 赠送笔记本 link 题意 有 n n n 个宿舍,每个宿舍 4 4 4 头奶牛,第 i i i 个宿舍有 a i a_i ai​ 头牛有笔记本(每头牛的笔记本都不同)。现在所有奶…...

工业相机开发操作流程

建议按照如下的流程操作相机(其中有一些步骤是可选的,已经标明): 一、载入SDK的动态链接库档MVCAMSDK.DLL。可以使用动态或者静 态加载两种方式。 如果使用C/C进行开发,在工程引用 CameraApi.h头文件(位于安装目录的SDK/DEMO/VC/include中)和…...

DeepSeek-R1 模型及GRPO算法学习

总结DeepSeek-R1 模型算法,并对其中的GRPO算法做一些学习补充。 DeepSeek-R1 论文总结 提出了通过强化学习提升大语言模型推理能力的方法,开发出 DeepSeek-R1-Zero 和 DeepSeek-R1 模型,在多个推理任务上表现出色,并开源模型推动…...

Vue 3.0打造响应式用户界面的新方式

1 简介 Vue.js 是一个用于构建用户界面的渐进式框架。Vue 3.0 是其最新版本,引入了许多新特性和改进,使得开发者能够更高效地构建响应式的Web应用程序。本文将带你深入了解如何使用Vue 3.0来打造响应式用户界面,并通过实际案例和代码示例帮助你快速上手。 2 环境搭建 要开…...

爬虫基础(二)Web网页的基本原理

一、网页的组成 网页由三部分构成:HTML、JavaScript、CSS。 (1)HTML HTML 相当于网页的骨架,它通过使用标签来定义网页内容的结构。 举个例子: 它把图片标签为img、把视频标签为video,然后组合到一个界面…...

Kotlin开发(六):Kotlin 数据类,密封类与枚举类

引言 想象一下,你是个 Kotlin 开发者,敲着代码忽然发现业务代码中需要一堆冗长的 POJO 类来传递数据。烦得很?别急,Kotlin 贴心的 数据类 能帮你自动生成 equals、hashCode,直接省时省力!再想想需要多种状…...

我的AI工具箱Tauri+Django内容生产介绍和使用

在现代内容生产环境中,高效、自动化的工具能够显著提升生产力,降低人工成本。Tauri 与 Django 结合打造的工作箱,集成了强大的 音频处理、视频剪辑、内容下载 以及 AI 文章撰写 等模块,帮助用户在多媒体内容生产的各个环节实现高效…...

openssl 生成证书 windows导入证书

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...

AJAX笔记入门篇

黑马程序员视频地址: 黑马程序员前端AJAX入门到实战全套教程https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p2https://www.bilibili.com/video/BV1MN411y7pw?vd_source…...

DOM操作中childNodes与children的差异及封装方案

引言 在JavaScript的DOM操作中,childNodes和children是开发者常用的属性,但它们在浏览器中的行为差异可能导致兼容性问题。尤其是在处理空白符(如换行符\n)时,某些浏览器(如Chrome和Edge)会将空…...

数据分析系列--④RapidMiner进行关联分析(案例)

一、核心概念 1.1项集(Itemset) 1.2规则(Rule) 1.3支持度(Support) 1.3.1 支持度的定义 1.3.2 支持度的意义 1.3.3 支持度的应用 1.3.4 支持度的示例 1.3.5 支持度的调整 1.3.6 支持度与其他指标的…...

危机13小时:追踪一场GitHub投毒事件

事件概要 自北京时间 2024.12.4 晚间6点起, GitHub 上不断出现“幽灵仓库”,仓库中没有任何代码,只有诱导性的病毒文件。当天,他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒,等待不…...

LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略

LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略 目录 STORM系统简介 1、Co-STORM 2、更新新闻 STORM系统安装和使用方法 1、安装 pip安装 直接克隆GitHub仓库 2、模型和数据集 两个数据集 FreshWiki数据集 WildSeek数据集 支持…...

使用QSqlQueryModel创建交替背景色的表格模型

class UserModel(QSqlQueryModel):def __init__(self):super().__init__()self._query "SELECT name, age FROM users"self.refresh()def refresh(self):self.setQuery(self._query)# 重新定义data()方法def data(self, index, role): if role Qt.BackgroundRole…...

低代码产品插件功能一览

下图是统计的目前市面上流行的低代码、零代码产品的插件功能。 产品名称 产品类型 官方插件数量 支持拓展 官方插件功能 宜搭 零代码 3 暂不支持 云打印、CAD看图、打印表单详情 微搭 低代码 1 暂不支持 小程序 明道云 低代码 2 支持 视图、工作流节点 简道…...

buu-rip-好久不见26

简单的栈溢出,找到后面函数和输入的个数即可...

Flutter 新春第一弹,Dart 宏功能推进暂停,后续专注定制数据处理支持

在去年春节,Flutter 官方发布了宏(Macros)编程的原型支持, 同年的 5 月份在 Google I/O 发布的 Dart 3.4 宣布了宏的实验性支持,但是对于 Dart 内部来说,从启动宏编程实验开始已经过去了几年,但…...

新手项目管理的实用工具推荐

项目启动的实用工具推荐 1. MindManager MindManager 是一款功能强大且广受欢迎的思维导图工具,对于项目启动阶段的新手而言,它就像是一位贴心的 “思路梳理助手”。在项目启动初期,各种信息和想法往往杂乱无章地充斥在脑海中,而…...

2025一区新风口:小波变换+KAN!速占!

今天给大家分享一个能让审稿人眼前一亮,好发一区的idea:小波变换KAN! 一方面:KAN刚中稿ICLR25,正是风口上,与小波变换的结合还处于起步阶段,正是红利期,创新空间广阔。 另一方面&a…...

Django ORM解决Oracle表多主键的问题

现状 以Django 3.2为例 Django ORM 设计为默认使用单一主键(通常是自增的 id 字段),这一选择主要基于以下核心原因: 简化ORM设计与操作 统一访问方式外键关联简化 避免歧义冲突 主键语义明确防止隐式依赖 性能与数据库兼容 索引…...

solidity高阶 -- 线性继承

Solidity是一种面向合约的高级编程语言,用于编写智能合约。在Solidity中,多线继承是一个强大的特性,允许合约从多个父合约继承属性和方法。本文将详细介绍Solidity中的多线继承,并通过不同的实例展示其使用方法和注意事项。 在Sol…...

无公网IP 外网访问 本地部署夫人 hello-algo

hello-algo 是一个为帮助编程爱好者系统地学习数据结构和算法的开源项目。这款项目通过多种创新的方式,为学习者提供了一个直观、互动的学习平台。 本文将详细的介绍如何利用 Docker 在本地安装部署 hello-algo,并结合路由侠内网穿透实现外网访问本地部署…...

系统思考—蝴蝶效应

“个体行为的微小差异,可能在系统中引发巨大且不可预测的结果。” — 诺贝尔经济学得主托马斯谢林 我们常说,小变动带来大影响,这种现象,在复杂系统理论中被称为“蝴蝶效应”:即使极小的变化,也能在动态系…...

钉钉群机器人设置——python版本

钉钉群机器人设置——python版本 应用场景钉钉界面操作程序开发效果展示 应用场景 由于工作需要,很多项目执行程序后出现报错信息无法第一时间收到,因此实时预警对于监控程序还是有必要。(仅个人观点) 参考文档及博客&#xff1a…...

sem_wait的概念和使用案列

sem_wait 是 POSIX 标准中定义的一个用于同步的函数,它通常用于操作信号量(semaphore)。信号量是一个整数变量,可以用来控制对共享资源的访问。在多线程编程中,sem_wait 常用于实现线程间的同步。 概念 sem_wait 的基…...