欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

2025/6/10 9:35:55 来源:https://blog.csdn.net/m0_53827889/article/details/148543202  浏览:    关键词:PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

在构建任何动态、数据驱动的Web API时,一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说,深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言,以及学会如何在Python中操作数据库,是构建强大应用的关键一步。

本文将带你探索关系型数据库(以PostgreSQL为例)的核心概念,精通SQL的关键操作,并学习如何使用Python的psycopg2驱动连接和操作PostgreSQL,为你的FastAPI项目打下坚实的数据基础。

1. 关系型数据库与数据库管理系统(DBMS) (以PostgreSQL为例)

关系型数据库因其清晰的结构、强大的数据一致性保障以及成熟的生态系统而备受青睐。其核心思想是将数据组织成一张张表格 (Table)。这些表格就像电子表格一样,有行有列。更重要的是,这些表格之间可以通过预定义的关系 (Relationship) 相互连接。例如,在一个博客系统中,用户 (users) 表和 帖子 (posts) 表可以通过“用户ID”建立联系,表明哪篇帖子是由哪个用户创建的。这种结构化的方式使得数据的查询、维护和管理变得高效且可靠。

我们通常不会直接去操作底层的数据库文件。取而代之的是,我们会通过一个中间件软件——数据库管理系统 (DBMS) 来与数据库交互。DBMS可以看作是数据库的“大管家”或“智能大脑”,它负责接收我们(或我们的应用程序)发出的指令(比如“帮我查一下所有价格大于100的商品”、“新建一条用户记录”),执行这些操作,并将结果返回。PostgreSQL 就是一款功能强大、开源且高度可扩展的关系型DBMS,深受开发者喜爱。

安装PostgreSQL后,你可以在一台服务器上创建多个相互独立的数据库实例 (Database Instance),每个实例都可以服务于不同的应用程序。这意味着你可以为你的FastAPI博客项目创建一个专属的数据库,而不会与其他应用(比如一个电商系统)的数据混淆。通常,PostgreSQL安装时会默认创建一个名为 postgres 的数据库,它可以作为连接到PostgreSQL服务器的初始入口点。

2. SQL:与数据库“流利对话”的语言

无论你选择哪款关系型DBMS——PostgreSQL、MySQL、Oracle还是SQL Server——它们都“听得懂”一门通用的语言:SQL (Structured Query Language)。SQL是与关系型数据库进行交互的标准语言。虽然不同DBMS在SQL的某些高级特性或方言上可能存在细微差别,但其核心语法和概念是高度一致的。

对于PostgreSQL,我们可以使用像PG Admin这样的可视化管理工具,在其查询工具中直接编写和执行SQL命令来创建表、插入数据、查询信息等。但更常见的是,我们的应用程序(比如用FastAPI构建的API)需要通过代码来执行这些SQL操作。

3. Python与PostgreSQL的桥梁:psycopg2 驱动

要在Python应用程序中与PostgreSQL数据库通信,我们需要一个数据库驱动程序 (Database Driver)。对于PostgreSQL,psycopg2 是最流行和功能最完善的Python驱动之一。它实现了Python数据库API规范v2.0 (DB-API 2.0),允许我们用Python代码连接到PostgreSQL、执行SQL语句并处理结果。

安装 psycopg2:
在你的Python虚拟环境中,可以通过pip轻松安装:

pip install psycopg2-binary

(psycopg2-binary 包包含了预编译的二进制文件,安装更便捷。)

建立连接与创建游标:
在FastAPI应用启动时(或者在需要操作数据库的模块中),你需要建立到PostgreSQL的连接。一个典型的连接过程如下:

# main.py (部分代码,展示连接逻辑)
import psycopg2
from psycopg2.extras import RealDictCursor # 让查询结果以字典形式返回,键为列名
import time# 尝试连接数据库,如果失败则重试
while True:try:conn = psycopg2.connect(host="localhost",      # 数据库服务器地址database="fastapi_db", # 你创建的数据库名称user="your_postgres_user", #你的PostgreSQL用户名password="your_postgres_password", # 你的PostgreSQL密码cursor_factory=RealDictCursor # 重要!使查询结果返回字典而非元组)cur = conn.cursor() # 创建一个游标对象,用于执行SQL语句print("数据库连接成功!")break # 连接成功,跳出循环except Exception as error:print("连接数据库失败")print("错误详情: ", error)time.sleep(2) # 等待2秒后重试
  • psycopg2.connect(): 这是建立连接的核心函数,需要提供主机、数据库名、用户名和密码。
  • cursor_factory=RealDictCursor: 这是一个非常有用的配置。默认情况下,psycopg2查询返回的是元组 (tuple) 列表。使用 RealDictCursor,查询结果会变成字典 (dict) 列表,其中键是数据库表的列名,这使得在代码中通过列名访问数据更加直观和方便,尤其是在构建API响应时。
  • conn.cursor(): 连接成功后,我们通过 conn.cursor() 方法创建一个游标 (Cursor) 对象。游标就像一个在数据库结果集上移动的指针,我们通过它来执行SQL语句和获取结果。
  • 错误处理与重试: 在实际应用中,数据库连接可能会因为网络问题、数据库服务未启动等原因失败。上面的代码片段包含了一个简单的循环重试机制,这在生产环境中是常见的做法(尽管更健壮的方案可能还会涉及更复杂的重试策略和日志记录)。

现在我们有了连接和游标,就可以开始学习如何用SQL结合psycopg2来操作数据了。

4. SQL核心操作:与数据互动

4.1 数据查询:SELECT语句 —— 探索你的数据宝藏

SELECT 是SQL中使用频率最高的命令,它的使命是从数据库中检索你需要的数据。

  • 选择所有列 (*):

    SELECT * FROM products;
    

    在Python中使用psycopg2执行:

    # 【示例】在FastAPI路径操作中获取所有帖子
    @app.get("/posts")
    def get_all_posts():cur.execute("SELECT * FROM posts;") # 执行SQL查询posts_data = cur.fetchall()        # 获取所有查询结果return posts_data                  # FastAPI会自动将字典列表转为JSON
    

    cur.execute(sql_query) 用于执行SQL语句。对于SELECT查询,执行后结果并不会立即返回到Python变量中,而是存储在游标内部。你需要使用获取方法(如 fetchall(), fetchone(), fetchmany())来检索数据。

    • cur.fetchall(): 获取查询结果集中的所有行。
    • cur.fetchone(): 获取查询结果集中的下一行,如果结果集为空或已读取完毕,则返回None
    • cur.fetchmany(size): 获取指定数量的行。
  • 选择特定列:

    SELECT id, name, price FROM products;
    
    cur.execute("SELECT id, title, content FROM posts WHERE published = TRUE;")
    published_posts = cur.fetchall()
    
  • 列别名 (AS): 在SQL中使用AS为列指定别名,可以使API响应的JSON字段名更友好。

    SELECT id AS product_id, name AS product_name FROM products;
    

    如果使用了 RealDictCursor,返回的字典键名会自动使用你指定的别名。

4.2 数据过滤:WHERE子句 —— 精准定位信息

WHERE子句是SELECT语句的得力助手,它允许你根据特定条件筛选数据,只返回那些你真正感兴趣的行。

  • 相等条件 (=):

    SELECT * FROM products WHERE id = 10;
    SELECT * FROM products WHERE name = '智能电视'; -- 文本值用单引号
    

    在Python中使用psycopg2时,强烈建议使用参数化查询 (Parameterized Queries) 来防止SQL注入攻击

    # 【示例】在FastAPI路径操作中获取特定ID的帖子
    @app.get("/posts/{post_id}")
    def get_one_post(post_id: int):cur.execute("SELECT * FROM posts WHERE id = %s;", (str(post_id),)) # 使用%s占位符post = cur.fetchone()if not post:raise HTTPException(status_code=404, detail="帖子未找到")return post
    
    • 参数化查询 (%s): 在SQL语句中使用 %s 作为占位符,然后将实际的值作为元组传递给 cur.execute() 的第二个参数。psycopg2会自动处理值的转义,从而保护你的应用免受SQL注入的威胁。切勿直接将用户输入拼接到SQL字符串中!
  • 比较运算符 (>, <, >=, <=, <>!=):

    SELECT * FROM products WHERE price > 50.00;
    
    cur.execute("SELECT * FROM posts WHERE rating > %s;", (3,))
    highly_rated_posts = cur.fetchall()
    
  • 逻辑运算符 (AND, OR):

    SELECT * FROM products WHERE inventory > 0 AND price < 100.00;
    
    cur.execute("SELECT * FROM posts WHERE published = %s AND owner_id = %s;", (True, current_user_id))
    
  • IN 运算符: 匹配列表中的任意一个值。

    SELECT * FROM products WHERE category_id IN (1, 5, 7);
    
    category_ids = (1, 5, 7) # 必须是元组
    cur.execute("SELECT * FROM products WHERE category_id IN %s;", (category_ids,))
    

    注意:当IN操作符与%s占位符一起使用时,值本身需要是一个元组。

  • LIKE 运算符 (模式匹配): 与通配符 % (匹配零或多个字符) 和 _ (匹配单个字符) 结合使用。

    SELECT * FROM products WHERE name LIKE '智能%'; -- 名称以“智能”开头
    
    search_term = "快速入门"
    cur.execute("SELECT * FROM posts WHERE title LIKE %s;", ('%' + search_term + '%',)) # 构造包含通配符的搜索模式
    search_results = cur.fetchall()
    
4.3 数据排序:ORDER BY子句 —— 让结果井然有序

ORDER BY 用于指定查询结果的排序方式。

SELECT * FROM products ORDER BY price ASC; -- 按价格升序 (ASC是默认)
SELECT * FROM products ORDER BY created_at DESC; -- 按创建时间降序 (最新在前)
cur.execute("SELECT * FROM posts ORDER BY created_at DESC, title ASC;")
sorted_posts = cur.fetchall()
4.4 结果限制与偏移:LIMITOFFSET —— 高效分页的秘诀

在处理大量数据时,一次性返回所有结果是不明智的。LIMITOFFSET 是实现分页功能的关键。

SELECT * FROM products ORDER BY id LIMIT 10 OFFSET 20; -- 跳过前20条,然后取10条
page_size = 10
page_number = 3
offset_val = (page_number - 1) * page_size
cur.execute("SELECT * FROM posts ORDER BY id LIMIT %s OFFSET %s;", (page_size, offset_val))
paginated_posts = cur.fetchall()
4.5 数据修改操作:改变数据库的状态
  • 插入数据:INSERT INTO 语句

    INSERT INTO products (name, price, inventory, category_id)
    VALUES ('新款耳机', 199.99, 50, 3);
    

    在Python (psycopg2)中插入数据并获取新插入记录的ID (或其他列):

    # 【示例】在FastAPI路径操作中创建新帖子
    @app.post("/posts", status_code=201)
    def create_new_post(post_payload: PostSchema): # PostSchema是Pydantic模型cur.execute("""INSERT INTO posts (title, content, published, owner_id)VALUES (%s, %s, %s, %s) RETURNING *;""", (post_payload.title, post_payload.content, post_payload.published, current_user_id))newly_created_post = cur.fetchone()conn.commit() # !!! 重要:提交事务,使更改永久生效 !!!return newly_created_post
    
    • RETURNING * (或 RETURNING id): 这是PostgreSQL的一个强大特性,允许你在执行INSERT (同样适用于UPDATEDELETE) 操作后立即返回被操作行的数据(比如新生成的ID),非常适合在API响应中返回新创建或修改后的资源。
    • conn.commit(): 对于任何修改数据库的操作(INSERT, UPDATE, DELETE),执行完cur.execute()后,更改只是暂存在当前事务中。你必须调用 conn.commit() 才能将这些更改永久写入数据库。否则,当连接关闭或程序结束时,这些更改会丢失(回滚)。
    • conn.rollback(): 如果在事务过程中发生错误,你可以调用 conn.rollback() 来撤销当前事务中所有未提交的更改。
  • 更新数据:UPDATE 语句

    UPDATE products SET price = 209.99, inventory = 45 WHERE id = 123;
    
    # 【示例】更新帖子
    @app.put("/posts/{post_id}")
    def update_existing_post(post_id: int, post_payload: PostUpdateSchema):cur.execute("""UPDATE posts SET title = %s, content = %s, published = %sWHERE id = %s RETURNING *;""", (post_payload.title, post_payload.content, post_payload.published, str(post_id)))updated_post_data = cur.fetchone()if not updated_post_data:raise HTTPException(status_code=404, detail="要更新的帖子未找到")conn.commit()return updated_post_data
    
  • 删除数据:DELETE FROM 语句

    DELETE FROM products WHERE id = 789;
    
    # 【示例】删除帖子
    @app.delete("/posts/{post_id}", status_code=204)
    def delete_existing_post(post_id: int):cur.execute("DELETE FROM posts WHERE id = %s RETURNING *;", (str(post_id),))deleted_post_data = cur.fetchone()if not deleted_post_data:raise HTTPException(status_code=404, detail="要删除的帖子未找到")conn.commit()# 对于204 No Content,通常不返回响应体return Response(status_code=status.HTTP_204_NO_CONTENT)
    

    务必小心使用 DELETE 如果忘记了WHERE子句,它会删除表中的所有数据!

4.6 SQL 最佳实践回顾
  • 大写SQL关键字:提高可读性 (如 SELECT, FROM, WHERE)。
  • 分号结尾psycopg2通常不需要在execute()的字符串末尾加分号,但如果你在PG Admin或其他工具中写SQL,记得加上。
  • 参数化查询永远,永远,永远 使用参数化查询 (%s 占位符和值元组) 来防止SQL注入,尤其是在处理用户输入时。
  • 事务管理 (commit/rollback):理解何时以及为何需要提交或回滚事务。

5. 数据库设计基础:为你的数据建模

良好的数据库设计是构建健壮、可维护应用的核心。

5.1 表 (Tables)、行 (Rows) 与列 (Columns)
  • 表 (Table):代表应用中的一个核心“事物”或“概念”,如 users, posts, comments
  • 列 (Column):表的属性,描述“事物”的特征,如 users 表的 username, email, created_at
  • 行 (Row):表中的一条记录,代表一个具体的“事物”实例,如一个特定的用户。
5.2 数据类型 (Data Types)

为列选择正确的数据类型至关重要,它确保了数据的准确性、存储效率和查询性能。PostgreSQL提供了丰富的数据类型:

  • 数值类型: INTEGER (整数), BIGINT (大整数), NUMERIC(precision, scale) (精确小数), REAL (单精度浮点), DOUBLE PRECISION (双精度浮点)。
  • 文本类型: VARCHAR(n) (可变长度字符串,最大长度n), TEXT (可变长度字符串,无显式长度限制)。
  • 布尔类型: BOOLEAN ( TRUEFALSE )。
  • 日期/时间类型: DATE (日期), TIME (时间), TIMESTAMP (时间戳,不带时区), TIMESTAMPTZ (或 TIMESTAMP WITH TIME ZONE, 时间戳,带时区 - 推荐使用,以避免时区混淆)。
  • 其他: UUID (通用唯一标识符), JSON, JSONB (二进制JSON,查询性能更好), ARRAY (数组) 等。
5.3 主键 (Primary Key - PK) —— 每行的唯一“身份证”

主键是一列或多列的组合,其值能唯一标识表中的每一行。

  • 唯一性 (UNIQUE):主键值在表中不能重复。
  • 非空性 (NOT NULL):主键值不能为空。
  • 常用选择: 通常,我们会为每个表添加一个名为 id 的列,类型为 SERIAL (自动递增的整数,PostgreSQL特有,相当于 INTEGER NOT NULL DEFAULT nextval('sequence_name')) 或 BIGSERIAL,并将其设为主键。或者使用 UUID 类型并生成UUID作为主键。
    CREATE TABLE users (id SERIAL PRIMARY KEY, -- 自动递增的整数主键username VARCHAR(50) UNIQUE NOT NULL,email VARCHAR(100) UNIQUE NOT NULL
    );
    
5.4 外键 (Foreign Key - FK) —— 表与表之间的“纽带”

外键是表中的一列(或多列),其值引用了另一张表的主键。它用于建立和强制表之间的链接或关系。

  • 定义关系: 例如,posts 表可以有一个 owner_id 列作为外键,引用 users 表的 id 列,表明该帖子属于哪个用户。
  • 数据完整性 (Referential Integrity): 外键有助于维护数据的一致性。例如,不能创建一个 owner_id 指向一个不存在的用户的帖子。
  • ON DELETE 策略: 当被引用的父表行(如 users 表中的某用户)被删除时,需要定义子表(如 posts 表中该用户的所有帖子)如何响应:
    • CASCADE: 父行删除,所有引用它的子行也自动删除(例如,删除用户,其所有帖子也删除)。
    • SET NULL: 父行删除,子行中的外键列被设置为 NULL (前提是该外键列允许为NULL)。
    • SET DEFAULT: 父行删除,子行中的外键列被设置为其默认值。
    • RESTRICT / NO ACTION (通常是默认行为): 如果存在引用的子行,则阻止删除父行。
    CREATE TABLE posts (id SERIAL PRIMARY KEY,title TEXT NOT NULL,content TEXT NOT NULL,owner_id INTEGER NOT NULL,CONSTRAINT fk_posts_owner_id FOREIGN KEY (owner_id) -- 定义外键约束REFERENCES users (id) ON DELETE CASCADE -- 如果用户被删除,其帖子也级联删除
    );
    
5.5 约束 (Constraints) —— 数据的“规则守护者”

约束是施加在表或列上的规则,用于限制可以存入的数据,进一步保证数据的质量。

  • NOT NULL: 列值不能为空。
  • UNIQUE: 列中的所有值必须唯一(一个表可以有多个UNIQUE约束)。
  • PRIMARY KEY: 隐含 NOT NULLUNIQUE
  • FOREIGN KEY: 确保引用完整性。
  • CHECK: 定义一个布尔表达式,插入或更新的行必须满足该表达式(例如 CHECK (price > 0))。
  • DEFAULT: 如果插入时未提供值,则使用此默认值(例如 published BOOLEAN DEFAULT FALSE)。

总结:数据驱动FastAPI的坚实一步

通过本文的探索,你不仅深入了解了关系型数据库(特别是PostgreSQL)的基石,掌握了SQL这门与数据对话的通用语言的核心操作,更重要的是,你学会了如何使用Python的psycopg2驱动程序作为桥梁,在你的FastAPI应用中实际连接并操作PostgreSQL数据库。从基础的SELECT查询到复杂的数据修改,再到数据库设计的关键概念如主键、外键和约束,这些都是构建任何数据驱动型FastAPI应用不可或缺的知识。

熟练运用psycopg2执行参数化查询、管理事务,并结合良好的数据库设计原则,将使你能够高效地为你的FastAPI应用构建强大、安全且可维护的后端数据支持。

下一步建议:从原生SQL到ORM的飞跃

虽然直接使用psycopg2和原生SQL语句能让你对数据库操作有非常清晰的控制,但在大型项目中,直接编写大量SQL可能会变得繁琐且容易出错。

因此,我强烈建议你接下来深入学习对象关系映射器 (Object-Relational Mapper, ORM),特别是与FastAPI无缝集成的 SQLAlchemy ORM。ORM允许你使用Python类和对象来定义数据库表和行,用Python方法来执行数据库查询和操作,从而在很大程度上抽象掉了原生SQL的编写。这不仅能提高开发效率,还能使代码更具Pythonic风格,更易于维护和测试。

同时,不要停止实践SQL本身!继续探索更高级的SQL特性,例如:

  • JOIN 操作: 这是关系型数据库的精髓所在,允许你根据表之间的关系,从多张表中组合和检索数据(例如,获取一篇帖子及其作者的用户名和邮箱)。
  • 聚合函数 (COUNT, SUM, AVG, MAX, MIN) 与 GROUP BY: 用于数据统计和分析。
  • 子查询 (Subqueries): 在一个查询中嵌套另一个查询。
  • 窗口函数 (Window Functions): 用于执行复杂的分析计算。

掌握这些,你的数据处理能力将更上一层楼,为构建功能更丰富的FastAPI应用奠定更坚实的基础。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词