盘一下如何用SQLAlchemy

发布于:2025-06-26 ⋅ 阅读:(23) ⋅ 点赞:(0)

# 引言

还是因为要开发fastapi应用,想操作数据库,就得考虑方式SQL注入的问题,不能跟这篇帖子展示的那样直接拼接sql:
https://blog.csdn.net/weixin_42988262/article/details/148769641?spm=1001.2014.3001.5501
而是得用SQLAlchemy,所以,这篇盘一下如何用SQLAlchemy。

# 先找找教程

直接去https://pypi.org/project/看看有没有资料,很稳:
https://pypi.org/project/SQLAlchemy/

【省时预警】:肯定是原文写得好,既然链接都贴出来了,我这里看懂啥就写啥,本预警不再重复

SQLAlchemy可以用来把数据库表的记录映射成对象;
能把SQL的关系型查询能力,比如join、子查询、关联这样的操作完整得给到python代码
。。。文档在这里了:

https://docs.sqlalchemy.org/en/20/,页面这个样:

先看红框里得这个https://docs.sqlalchemy.org/en/20/orm/quickstart.html

# quickstart.html页面

链接https://docs.sqlalchemy.org/en/20/orm/quickstart.html页面这个样:

先创建库表对应的类

先声明一些“模块级别的数据结构”,这些类对应着数据库表,也叫“声明式映射”(Declarative Mapping)。既是一个python类,也是“数据库元数据”,代码,直接从页面上复制的哈:

from typing import List
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "user_account"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    fullname: Mapped[Optional[str]]
    addresses: Mapped[List["Address"]] = relationship(
        back_populates="user", cascade="all, delete-orphan"
    )
    def __repr__(self) -> str:
        return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"

class Address(Base):
    __tablename__ = "address"
    id: Mapped[int] = mapped_column(primary_key=True)
    email_address: Mapped[str]
    user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
    user: Mapped["User"] = relationship(back_populates="addresses")
    def __repr__(self) -> str:
        return f"Address(id={self.id!r}, email_address={self.email_address!r})"

Base是个父类,它是DeclarativeBase的子类,但没添加任何属性和方法,应该是为了抽象出一层来,后边方便一些。类User和Address都是Base的子类,分别对应库表user_account、address,__tablename__属性就是指示这个的。
这些语句都对应数据库表的列:

User类和Address类叫“private mapped classes”,

上图行号为15的那句 id: Mapped[int] = mapped_column(primary_key=True),Mapped用来声明列的“typing”

上图行号为17的那句fullname: Mapped[Optional[str]],“Optionnal”对应着数据库表的该列可为空

上图行号为16的那句,mapped_column()用来对应库表字段的其它信息,具体该咋对应,在这:
https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html#orm-declarative-mapped-column-type-map

所有列的属性,都用mapped_column()指令来处理,不仅仅是类型信息,约束信息也是用这个处理

SQLAlchemy的世界里有一个类叫Column,代表数据库表的列,只要是Column的子类,都可以用mapped_column()处理

Object Relational Mapper mapped类,比如User和Address,要求最少有一个列被声明为主键

SQLAlchemy的世界里有一个概念叫table metadata,以上图user类/Address类为例,红框圈起来的,加上__tablename__那行,就是表元数据,这里讲得更详细:
        https://docs.sqlalchemy.org/en/20/tutorial/metadata.html#tutorial-working-with-metadata
        上图user类/Address类就是“Annotated Declarative Table configuration”,“注解声明表配置”?

其它的映射变体,大多数都会用relationship()指令;这个指令用来说明两个ORM类之间的关系,
比如上面的例子,User.address属性把User类链接到Address,Address.user把Address类链接到User类。relationship()的更多介绍,在:
https://docs.sqlalchemy.org/en/20/tutorial/orm_related_objects.html#tutorial-orm-related-objects

最后,用到一个__repr__方法,这不是必须的,但对查bug很有用。具体得去看:
https://docs.sqlalchemy.org/en/20/orm/dataclasses.html#orm-declarative-native-dataclasses

创建引擎

这里的“引擎”是个“工厂”,可以产生新的数据库连接,也可以实现连接池重用连接的效果,页面上的示例为了方便用了一个memory-only SQLite数据库,就2行:

	from sqlalchemy import create_engine
	engine = create_engine("sqlite://", echo=True)

其中,echo=True意味着SQL的结果打印到标准输出
较全的介绍,在https://docs.sqlalchemy.org/en/20/tutorial/engine.html#tutorial-engine
sqlite官网https://www.sqlite.org/,本来还想就这个整理点儿笔记贴出来,发现暂时没必要多研究,先看看就行

导出建表语句

标题是Emit Create Table DDL,是“导出建表语句”的意思吗?

## 先准备一下环境

python3 -m pip install sqlalchemy -i https://mirrors.aliyun.com/pypi/simple

不知道这些是在说什么的,可以去看
https://blog.csdn.net/weixin_42988262/article/details/148684972?spm=1001.2014.3001.5501

## 按页面的说法准备代码试一试:

效果和页面上的差不多:

页面上说执行这段python代码会发生很多事情,想要知道完整一点儿的,得去看:https://docs.sqlalchemy.org/en/20/tutorial/metadata.html#tutorial-working-with-metadata

Create Objects and Persist

标题的意思是创建对象并持久化吗?反正现在可以往数据库插入记录了:创建User类和Address类的实例,使用从父类继承过来的__init__()方法,然后用一个叫Session的对象把这俩实例传给数据库,Session会使用engine与数据库交互。Session类的add_all()方法可以一次处理多个对象,Session类的commit()方法会实现flush的效果。

把页面上的代码拷贝过去,这是我这里新的t.py与之前比较的效果:

执行一下:

嗯,这段描述也说明了with会自动回收资源:

 

前一阵还刚盘了一下呢,哈哈哈:https://blog.csdn.net/weixin_42988262/article/details/148788924?spm=1001.2014.3001.5501

页面还很贴心得贴出来了Session类相关的链接,我这里不贴了。

简单查询

按页面添加这些代码:

这里,基本是见文知义了,不说了。。

执行一下试试,也页面上说的效果差不多:

我这里目标是使用mysql数据库,所以先不继续看这个页面了

# 还是决定看一下教程

觉得这样的时间值得花呢,教程链接https://docs.sqlalchemy.org/en/20/intro.html
SQLAlchemy分为SQL Toolkit和ORM,是一套让python和数据库交互的工具,主要组件如下图:

看图,SQLAlchemy可以分为ORM 和Core两部分

Core那一部分,包含SQL和数据库的集成、描述服务,最重要的部分是SQL Expression Language
SQL Expression Language提供组建SQL表达式的服务,这些表达式可以被数据库“执行”,并返回一个结果集。

ORM提供的是一种把宿主语言对象模型映射到数据库schema的途径。使用ORM时,SQL语句的创建大致与使用Core时相同,然而对于DML语句,会自动使用一种unit of work的模式。。
这种模式会“翻译”objects与DML语句之间的差异,。。

文档 分四部分:

SQLAlchemy Unified Tutorial

我这里去看的时候没做什么笔记,贴个链接应付一下吧https://docs.sqlalchemy.org/en/20/tutorial/index.html

这里是连接池相关的内容

https://docs.sqlalchemy.org/en/20/faq/connections.html#how-do-i-pool-database-connections-are-my-connections-pooled

上图Engin Configuration和Connection Pooling的链接是:
https://docs.sqlalchemy.org/en/20/core/engines.html
https://docs.sqlalchemy.org/en/20/core/pooling.html

就是说,SQLAlchemy已经把连接池这块的事情办了,不用操心了。

# 可以尝试搞一个用mysql的DEMO了

这里有相关内容:https://docs.sqlalchemy.org/en/20/dialects/mysql.html
直接上手吧,就参考quickstart页面上的那一个,准备环境:

创建了一下testdb:

没有mysql server的,可以去看:
https://blog.csdn.net/weixin_42988262/article/details/148716542?spm=1001.2014.3001.5501

代码贴一下:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from typing import List
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy import select


class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "user_account"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    fullname: Mapped[Optional[str]] = mapped_column(String(50))
    addresses: Mapped[List["Address"]] = relationship(
        back_populates="user", cascade="all, delete-orphan"
    )
    def __repr__(self) -> str:
        return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"

class Address(Base):
    __tablename__ = "address"
    id: Mapped[int] = mapped_column(primary_key=True)
    email_address: Mapped[str] =  mapped_column(String(50))
    user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
    user: Mapped["User"] = relationship(back_populates="addresses")
    def __repr__(self) -> str:
        return f"Address(id={self.id!r}, email_address={self.email_address!r})"

#engine = create_engine("sqlite://", echo=True)
engine = create_engine("mysql+pymysql://root:xxxxxxxx@127.0.0.1:3306/testdb?charset=utf8mb4", pool_size=1, pool_pre_ping=True)
Base.metadata.create_all(engine)

with Session(engine) as session:
    spongebob = User(
        name="spongebob",
        fullname="Spongebob Squarepants",
        addresses=[Address(email_address="spongebob@sqlalchemy.org")],
    )
    sandy = User(
        name="sandy",
        fullname="Sandy Cheeks",
        addresses=[
            Address(email_address="sandy@sqlalchemy.org"),
            Address(email_address="sandy@squirrelpower.org"),
        ],
    )
    patrick = User(name="patrick", fullname="Patrick Star")
    session.add_all([spongebob, sandy, patrick])
    session.commit()


session = Session(engine)
stmt = select(User).where(User.name.in_(["spongebob", "sandy"]))
for user in session.scalars(stmt):
    print(user)

代码里xxxxxxxx是msyql数据库的密码哈,试一下效果,很稳:


网站公告

今日签到

点亮在社区的每一天
去签到