SQLAlchemy 数据库操作

140 阅读6分钟

参考:space.bilibili.com/34615745618…

SQLAlchemy 特点

  • 使用 Python 编写的 ORM 框架
  • 支持 MS SQL、MySQL、Oracle、PGSQL、Sybase

下载

pip install sqlalchemy

连接 MySQL & 查询

import sqlalchemy

# 创建引擎 echo=True,打印数据库执行日志
# engine = sqlalchemy.create_engine('mysql+pymysql://root:root@localhost:3306/python_test', echo=True)
engine = sqlalchemy.create_engine('mysql+pymysql://root:root@localhost:3306/python_test')
# 创建链接
conn = engine.connect()

# 脚本创建查询
query_01 = sqlalchemy.text("select * from user")
result_set = conn.execute(query_01)

for row in result_set:
    print(row)

conn.close()
engine.dispose()

查询记录

select * from student

from db_init import engine, tb_person

with engine.connect() as conn:
    query = tb_person.select()
    result_set = conn.execute(query)

    # 结果集操作 1
    for row in result_set:
        print(row[0])
        print(row.name)

    #  操作 2 ,返回 list
    result = result_set.fetchall()

    # 只提取一行 (一般操作只有一个结果的集合)
    row = result_set.fetchone()

select * from student where ... and ...

from db_init import engine, tb_student
from sqlalchemy.sql import and_, or_

with engine.connect() as conn:
    # 简单条件查询
    # query = tb_student.select().where(tb_student.columns.birthday >= "2000-1-2").where(tb_student.columns.id < 4)

    # 复杂条件查询
    query = tb_student.select().where(
        and_(
            tb_student.columns.birthday >= "2000-1-2",
            tb_student.columns.id < 4
        )
    )
    result_set = conn.execute(query)

    result = result_set.fetchall()
    print(result)

面向数据表操作

创建表和增加记录

使用 meta_data.create_all(engine) 创建表,不常用

import sqlalchemy

engine = sqlalchemy.create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
meta_data = sqlalchemy.MetaData()

tb_person = sqlalchemy.Table(
    'person', meta_data,
    sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
    sqlalchemy.Column("name", sqlalchemy.String(128), unique=False, nullable=False),
    sqlalchemy.Column("birthday", sqlalchemy.Date, nullable=False),
)

tb_student = sqlalchemy.Table(
    'student', meta_data,
    sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
    sqlalchemy.Column("name", sqlalchemy.String(128), unique=False, nullable=False),
    sqlalchemy.Column("birthday", sqlalchemy.Date, nullable=False),
    sqlalchemy.Column("address", sqlalchemy.String(256)),
)

meta_data.create_all(engine)

插入记录

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, Date

engine = create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
meta = MetaData()

# 编程式建表
student = Table(
    'student', meta,
    Column("id", Integer, primary_key=True),
    Column("name", String(128), unique=False, nullable=False),
    Column("birthday", Date, nullable=False),
)

meta.create_all(engine)

# 编程式插入
insert = student.insert()
print(insert)

# insert one record

# insert_tom = insert.values(name="Tom", birthday="2000-1-1")
# with engine.connect() as conn:
#     result = conn.execute(insert_tom)
#     print(result.inserted_primary_key)
#     conn.commit()

# insert multiple records
with engine.connect() as conn:
    conn.execute(insert, [
        {"name": "Jack", "birthday": "2000-1-2"},
        {"name": "Mary", "birthday": "2000-1-3"},
        {"name": "Smith", "birthday": "2000-1-4"},
    ])
    conn.commit()

更新与删除

update

from db_init import engine, tb_student

with engine.connect() as conn:

    # demo-1 update all
    # update_query = tb_student.update().values(address="wuhan")

    # demo-2 update by
    update_query = tb_student.update().values(address="shanghai").where(tb_student.columns.id == 3)

    conn.execute(update_query)
    conn.commit()

delete

from db_init import engine, tb_student

with engine.connect() as conn:

    # demo-1 delete all
    # delete_query = tb_student.delete()

    # demo-2 update by
    delete_query = tb_student.delete().where(tb_student.columns.id == 7)

    conn.execute(delete_query)
    conn.commit()

关联表定义

db_init.py

import sqlalchemy

engine = sqlalchemy.create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
meta_data = sqlalchemy.MetaData()

tb_department = sqlalchemy.Table(
    "department", meta_data,
    sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
    sqlalchemy.Column("name", sqlalchemy.String(128))
)

tb_employee = sqlalchemy.Table(
    "employee", meta_data,
    sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
    sqlalchemy.Column("department_id", sqlalchemy.Integer, sqlalchemy.ForeignKey("department.id"), nullable=False),
    sqlalchemy.Column("name", sqlalchemy.String(255), nullable=False),
    sqlalchemy.Column("birthday", sqlalchemy.Date, default="2000-01-01")
)

meta_data.create_all(engine)

insert_records.py

from db_init import engine, tb_department, tb_employee

with engine.connect() as conn:
    conn.execute(tb_department.insert(), [
        {"name": "hr"},
        {"name": "it"}
    ])

    conn.execute(tb_employee.insert(), [
        {"department_id": 1, "name": "Jack"},
        {"department_id": 1, "name": "Tom"},
        {"department_id": 1, "name": "Mary"},
        {"department_id": 2, "name": "Smith"},
        {"department_id": 2, "name": "Rose"},
        {"department_id": 2, "name": "Leon"},
    ])

    conn.commit()

关联表查询

query.py

import sqlalchemy

from db_init import engine, tb_employee, tb_department

with engine.connect() as conn:
    join = tb_employee.join(tb_department, tb_department.columns.id == tb_employee.columns.department_id)
    # out join
    # query = sqlalchemy.select(join).where(tb_department.columns.name == "hr")
    """ 打印 emp + dep
    (1, 1, 'Jack', datetime.date(2000, 1, 1), 1, 'hr')
    (2, 1, 'Tom', datetime.date(2000, 1, 1), 1, 'hr')
    (3, 1, 'Mary', datetime.date(2000, 1, 1), 1, 'hr')
    """

    # left join : emp left join dep
    # query = sqlalchemy.select(tb_employee).select_from(join).where(tb_department.columns.name == "hr")
    """ 只打印 emp 表信息
    (1, 1, 'Jack', datetime.date(2000, 1, 1))
    (2, 1, 'Tom', datetime.date(2000, 1, 1))
    (3, 1, 'Mary', datetime.date(2000, 1, 1))
    """

    # left join : emp left join dep
    query = sqlalchemy.select(tb_department).select_from(join).where(tb_employee.columns.name == "Rose")
    """ 只打印 dep 表信息
    (2, 'it')
    """

    result = conn.execute(query).fetchall()
    for raw in result:
        print(raw)

面向对象操作

对比两种实体映射方式

面向数据表

image.png

面向对象 image.png

映射类定义和记录添加

定义数据模型类

db_init.py

from sqlalchemy import create_engine, Column, Integer, String, Date
from sqlalchemy.orm import declarative_base, sessionmaker

engine = create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
Base = declarative_base()

class Person(Base):
    __tablename__ = "person"

    id = Column(Integer, primary_key=True)
    name = Column(String(128), nullable=False)
    birthday = Column(Date)
    address = Column(String(255), nullable=True)


Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

insert.py

from db_init import Session, Person

session = Session()

# add a record
# p = Person(name="Amy", birthday="2000-01-01", address="wuhan")
# session.add(p)

# add a list of records
persons = [
    Person(name="Qiu", birthday="2000-01-02", address="wuhan"),
    Person(name="Liu", birthday="2000-01-03", address="wuhan"),
]
session.add_all(persons)

session.commit()

映射类查询与删除

query

from db_init import Session, Person

session = Session()

# first(): get first record
# person_first = session.query(Person).filter(Person.id == 1).first()
# person_first = session.query(Person).filter(Person.id == 100).first()
person_first = session.query(Person).filter(Person.id < 100).first()

# one(): get the only record
# person_one = session.query(Person).filter(Person.id < 100).one()  # except
person_one = session.query(Person).filter(Person.id == 1).one()

# scalar():
# person_scalar = session.query(Person).filter(Person.id < 100).scalar() # except
# person_scalar = session.query(Person).filter(Person.id == 1).scalar()
person_scalar = session.query(Person).filter(Person.id == 100).scalar()

if person_first:
    print(f"person_first    >>> name: {person_first.name}, birthday: {person_first.birthday}")

if person_one:
    print(f"person_one      >>> name: {person_one.name}, birthday: {person_one.birthday}")

if person_scalar:
    print(f"person_scalar   >>> name: {person_scalar.name}, birthday: {person_scalar.birthday}")

update

from db_init import Session, Person

session = Session()

# demo_1 single-update
person = session.query(Person).filter(Person.id == 1).one()
person.address = "chengdu"

# demo_2 single-update
update = session.query(Person).filter(Person.id == 1).update({
    Person.address: "shanghai"
})

# demo_3 multi-update
update = session.query(Person).filter(Person.id > 10).update({
    Person.address: "hangzhou"
})


session.commit()

新的映射方式

import datetime

from sqlalchemy import create_engine, String
from sqlalchemy.sql import func
from sqlalchemy.orm import declarative_base, sessionmaker, Mapped, mapped_column
from typing_extensions import Annotated

engine = create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
Base = declarative_base()

int_pk = Annotated[int, mapped_column(primary_key=True)]
unique = Annotated[str, mapped_column(String(128), nullable=False, unique=True)]
timestamp_default_now = Annotated[datetime.datetime, mapped_column(nullable=False, server_default=func.now())]


class Customer(Base):
    __tablename__ = "customer"

    """面向数据库操作
    id = Column(Integer, primary_key=True)
    name = Column(String(128), nullable=False)
    birthday = Column(Date)
    address = Column(String(255), nullable=True)
    """

    """面向类操作
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(128), nullable=False)
    birthday: Mapped[datetime.datetime]
    """

    """新的映射方式"""
    id: Mapped[int_pk]
    name: Mapped[unique]
    birthday: Mapped[datetime.datetime]
    city: Mapped[str] = mapped_column(String(128), nullable=True)
    create_time: Mapped[timestamp_default_now]

Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

一对多 ORM

db_init.py

import datetime

from sqlalchemy import create_engine, String, ForeignKey
from sqlalchemy.orm import declarative_base, sessionmaker, Mapped, mapped_column, relationship
from typing_extensions import Annotated

engine = create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
Base = declarative_base()

int_pk = Annotated[int, mapped_column(primary_key=True)]
unique = Annotated[str, mapped_column(String(128), nullable=False, unique=True)]
timestamp_not_null = Annotated[datetime.datetime, mapped_column(nullable=False)]


class Department(Base):
    __tablename__ = "department"

    id: Mapped[int_pk]
    name: Mapped[unique]

    def __repr__(self):
        return f"id: {self.id}, name: {self.name}"

class Employee(Base):
    __tablename__ = "employee"

    id: Mapped[int_pk]
    dep_id: Mapped[int] = mapped_column(ForeignKey("department.id"))
    name: Mapped[unique]
    birthday: Mapped[timestamp_not_null]

    # 关联内存中属性
    # department: Mapped[Department] = relationship()
    department: Mapped[Department] = relationship(lazy=False)

    def __repr__(self):
        return f"id: {self.id}, dep_id: {self.name}, birthday: {self.birthday}"

Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

query.py

from db_init import Session, Employee, Department

def insert_records(session):
    d1 = Department(name="hr")
    session.add(d1)
    # session.flush()

    # e1 = Employee(dep_id=d1.id, name="Jack", birthday="2000-01-01")
    e1 = Employee(department=d1, name="Jack", birthday="2000-01-01")
    session.add(e1)

    session.commit()

def select_records(session):
    emp = session.query(Employee).filter(Employee.id == 1).one()
    dep = emp.department
    print(emp)
    print(dep)


session = Session()

# insert_records(session)
if __name__ == '__main__':
    select_records(session)

不建议直接在定义中使用,但是可以了解的操作:

relationship 设置为 lazy=False image.png

select 一次,使用了 join

image.png

relationship 默认为 lazy=True

image.png

emp 和 dep 分开select image.png

多对多 ORM

db_init.py

import datetime

import sqlalchemy
from sqlalchemy import create_engine, String, ForeignKey
from sqlalchemy.orm import declarative_base, sessionmaker, Mapped, mapped_column, relationship
from typing_extensions import Annotated
from typing import List, Set

engine = create_engine("mysql+pymysql://root:root@localhost:3306/python_test", echo=True)
Base = declarative_base()

int_pk = Annotated[int, mapped_column(primary_key=True)]
unique = Annotated[str, mapped_column(String(128), nullable=False, unique=True)]
timestamp_not_null = Annotated[datetime.datetime, mapped_column(nullable=False)]
require_string = Annotated[str, mapped_column(String(128), nullable=False)]

# 定义关联关系
association_table = sqlalchemy.Table(
    "user_role", Base.metadata,
    sqlalchemy.Column("user_id", ForeignKey("user.id"), primary_key=True),
    sqlalchemy.Column("role_id", ForeignKey("role.id"), primary_key=True)
)


class User(Base):
    __tablename__ = "user"

    id: Mapped[int_pk]
    name: Mapped[unique]
    password: Mapped[require_string]

    roles: Mapped[Set["Role"]] = relationship(secondary=association_table, lazy=False)

    def __repr__(self):
        return f"id: {self.id}, dep_id: {self.name}"


class Role(Base):
    __tablename__ = "role"

    id: Mapped[int_pk]
    name: Mapped[unique]

    users: Mapped[Set["User"]] = relationship(secondary=association_table, lazy=True, back_populates="roles")

    def __repr__(self):
        return f"id: {self.id}, dep_id: {self.name}"


Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

query.py

from db_init import Session, User, Role

def insert_records(s):
    role1 = Role(name="admin")
    role2 = Role(name="operator")

    user1 = User(name="Jack", password="111")
    user2 = User(name="Tom", password="222")
    user3 = User(name="Mary", password="333")

    user1.roles.add(role1)
    user1.roles.add(role2)

    user2.roles.add(role1)
    user3.roles.add(role2)

    s.add_all([user1, user2, user3])
    s.commit()

def select_user(s):
    u = s.query(User).filter(User.id == 1).one()
    print(u)
    print(u.roles)

session = Session()

if __name__ == '__main__':
    # insert_records(session)
    select_user(session)

一对一 ORM

双向一对一

image.png 插入

image.png

基于 ORM 的查询

query.py ...

from db_init import Session, User, Role
from sqlalchemy.sql import select, delete, update, insert

def execute_query(query):
    result = session.execute(query)
    for row in result:
        print(row)

def select_single_target():
    query = select(User).order_by(User.id)
    execute_query(query)

def select_multiple():
    query = select(User, Role).join(Role.id)
    execute_query(query)


session = Session()

if __name__ == '__main__':
    select_single_target()
    select_multiple()

基于 ORM 的批量增删改

image.png

image.png

image.png

image.png

Session 与事务控制

还没有深入了解... 后续补充

image.png