添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
有爱心的书签  ·  iMX6ULL-OpenWRT - ...·  1 月前    · 
细心的登山鞋  ·  C# Split ...·  6 月前    · 
重情义的卤蛋  ·  从 Amazon S3 on ...·  1 年前    · 

用SQLAlchemy做ORM也有一段时间了,总结一下用过的一些地方。

连接数据库

SQLAlchemy通过一个url连接数据库,这个url包含了连接数据库相关的信息。

数据库连接URL

以MySQL为例,url的格式是 mysql+{driver}://{username}:{password}@{host}:{port}/{name}
其中 driver 是Python的数据库驱动,比如MySQL官方的数据库驱动 mysql-connector-python driver mysqlconnector
username 是数据库用户名; password 是密码; host 是数据库主机; port 是数据库端口; name 是数据库名。

创建数据库引擎,元数据以及会话等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import sessionmaker

db_url = "mysql+mysqlconnector://root:@localhost:3306/test"

# 通过数据库连接url创建数据库引擎
# 如果想回显SQLAlchemy操作数据库的日志,设置echo=True
engine = create_engine(db_url, echo=True)

# 通过数据库引擎绑定元信息
metadata = MetaData(engine)

# 通过绑定数据库引擎获取数据库会话类
Session = sessionmaker(bind=engine)

# 获取数据库会话
session = Session()

一个 Session 实例可以理解成一个数据库连接,通过它来操作数据库;
也可以将它理解成一个容器,各种对象的实例存储在其中。

数据库连接池

注意 :以上的连接数据库方式获取数据库连接是使用默认的数据库连接池的,
如果不想使用数据库连接池的话可以用以下方式创建数据库引擎。

1
2
3
4
5
from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool
db_url = "mysql+mysqlconnector://root:@localhost:3306/test"

engine = create_engine(db_url, poolclass=NullPool)

操作数据库

声明映射

在数据库中定义一个数据表对应在Python代码中则是定义一个类(也就是通常所说的Model),要维持从类到表的关系,首先就要先声明一个映射。

1
2
3
4
from sqlalchemy.ext.declarative import declarative_base

# 所有继承自`Base`的实体类都会自动映射数据表
Base = declarative_base()

然后就可以通过继承 Base 类来定义映射到数据表中的类了。

注意 declarative_base() 生成的类,该类的子类一般都必须与数据库中的一张表对应,
如果想扩充这个基类,让所有子类都能使用,可以用如下方法。

1
2
3
4
5
6
7
8
9
10
11
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class BaseModel(Base):

__abstract__ = True # 这样BaseModel类就不用对应数据库中的表了

# ... 扩充BaseModel类的方法或属性等

pass

定义数据表模型

现在尝试创建一个简单的数据表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sqlalchemy import Integer, String, Enum, Column
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Student(Base):

__tablename__ = "tb_student" # 数据表名

# 下面是各个字段
id = Column(Integer(), primary_key=True) # `id`字段,整数类型,主键,整数类型的主键默认为自动增长
name = Column(String(30), nullable=False) # `name`字段,字符串类型,最大长度30,设置为NOT NULL
gender = Column(Enum('male', 'female')) # `gender`字段,枚举类型

pass

Integer String Enum 等数据类型都是SQLAlchemy定义的,会根据使用的数据库不同而使用数据库中对应的类型,
你也可以使用特定数据库的特定类型,以MySQL为例。

1
from sqlalchemy.dialects.mysql import INTEGER, ENUM, VARCHAR, TINYINT, TEXT, BLOB, DATETIME

创建和删除数据表

上面的代码只是声明这么一个类 Student 映射数据表 tb_student ,创建数据表的操作需要如下代码。

1
2
3
# 创建所有定义的数据表,会在数据库中执行`CREATE TABLE ...`语句
# 已经存在的数据表不会做操作
Base.metadata.create_all(engine)

删除数据表也是一样的操作,值得一提的是,未在代码中定义的数据表和不存在的数据表是不会做删除操作的。

1
2
# 删除所有定义的数据表
Base.metadata.drop_all(engine)

数据状态

以ORM方式来对数据库中的数据做增删查改操作是通过 Session 实例来完成的,
在学习了解如何以ORM方式操作数据之前首先我们要对数据的状态有个基本的了解。

首先在ORM中,数据库中的 数据表 对应于Python中的 ,而 数据表中的记录 对应于 类的实例对象
因此,对数据表中的记录进行增删查改在Python中实际上就是对实例对象的操作。

数据实例对象有四种状态,分别是

  • Transient - (瞬时的)

    表示该实例对象不在session中,当然也没有保存到数据库中,
    主键一般情况下为 None (如果一个 Persistent 状态的对象进行事务回滚后虽然主键有值,但却是 Transient 状态)。

  • Pending - (挂起的)

    调用 session.add() 后, Transient 状态的对象就会变成 Pending 状态的对象,这个时候它只是在 session 中,
    并没有保存到数据库,因此主键依旧为 None
    只有触发了 session.flush() 操作才会保存到数据库使得主键有值,比如查询操作就会触发flush。

  • Persistent - (持久的)

    session 和数据库中都有对应的记录存在,为持久状态。

  • Detached - (游离的)

    数据库中可能有记录,但是 session 中不存在。对这种对象的操作不会触发任何SQL语句。

    要查看数据对象的状态可以用如下方式

    1
    2
    3
    4
    >>> from sqlalchemy import inspect
    >>> status = inspect(data_object)
    >>> status.persistent
    True

    SQLAlchemy关于数据实例对象状态的官方文档

    数据的简单操作

    以下面的数据表为例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from sqlalchemy import Integer, String, Enum, Column
    from sqlalchemy.ext.declarative import declarative_base

    Base = declarative_base()

    class User(Base):

    __tablename__ = 'tb_user'

    id = Column(Integer(), primary_key=True)
    name = Column(String(30), nullable=False)
    gender = Column(Enum('male', 'famale'))

    def __repr__(self):
    return "<User(id={})>".format(self.id)

    pass

    session 来表示数据库会话实例。

    查询操作

    查询子句使用 session .query() 方法来获取 Query 查询对象。
    查询对象能够使用一些方法来对应一些查询子句,比如 .order_by() .limit() .filter() 等,高级的查询后面会专门讲。

    查询对象有这么几种方法 .one() .all() .scalar() .one_or_none() .get() ,以及 .first() 等。
    下面对这几个方法的用法及效果做简单解释。

  • .all()

    返回查询到的所有的结果。
    这个方法比较危险的地方是,如果数据量大且没有使用 limit 子句限制的话,所有的结果都会加载到内存中。
    它返回的是一个 列表 ,如果查询不到任何结果,返回的是空列表。

  • .first()

    返回查询到的第一个结果, 如果没有查询到结果,返回 None

  • .scalar()

    这个方法与 .one_or_none() 的效果一样。
    如果查询到很多结果,抛出 sqlalchemy.orm.exc.MultipleResultsFound 异常。
    如果只有一个结果,返回它,没有结果返回 None

  • .one()

    如果只能查询到一个结果,返回它,否则抛出异常。
    没有结果时抛 sqlalchemy.orm.exc.NoResultFound ,有超过一个结果时抛 sqlalchemy.orm.exc.MultipleResultsFound

  • .one_or_none()

    比起 .one() 来,区别只是查询不到任何结果时不再抛出异常而是返回 None

  • .get()

    这是个比较特殊的方法。它用于根据主键来返回查询结果,因此它有个参数就是要查询的对象的主键。
    如果没有该主键的结果返回 None ,否则返回这个结果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 查询所有的User对象
    session.query(User).all()
    # 查询按照主键升序排序后的第一个User对象
    session.query(User).order_by(User.id.asc()).first()
    # 查询指定user_id为主键的对象
    session.query(User).get(user_id)
    session.query(User).filter(User.id == user_id).scalar()
    session.query(User).filter(User.id == user_id).one_or_none()
    session.query(User).filter(User.id == user_id).one() # 如果查询不到会抛出异常,前面三个查询不到只是返回None

    增加操作

    删除操作

    修改操作

  •