Python -- ORM(SQLAlchemy库)

Python ORM框架中SQLAlchemy库的查询操作教程 SQLAlchemy是一个强大的Python ORM(对象关系映射)库,它提供了一种将关系型数据库与Python对象之间进行映射的方式。在本教程中,我们将介绍SQLAlchemy库的查询操作,包括如何执行简单查询、过滤数据、排序结果以及执行聚合函数等。首先,我们需要安装SQLAlchemy库。安装完成后,我们可以开始使用SQLAlchemy进行查询操作。 阅读详情

ORM

  • ORM,对象关系映射,对象和关系之间的映射,使用面向对象的方式来操作数据库
关系模型和Python对象之间的映射
table => class ,表映射为类
row => object ,行映射为实例
column => property ,字段映射为属性

举例

有表student,字段为id int,name varchar,age int

  • 映射到Python为
class Student: 
	id = ?某类型字段 
	name = ?某类型字段 
	age = ?某类型字段

最终得到实例
class Student: 
	def __init__(self):
		self.id = ?
		self.name = ?
		self.age = ?

SQLALchemy

  • SQLAlchemy是一个ORM框架

安装

$ pip install sqlalchemy

文档

官方文档 http://docs.sqlalchemy.org/en/latest/
查看版本

import sqlalchemy
print(sqlalchemy.__version__)# 1.3.5

开发

  • SQLAlchemy内部使用了连接池

创建连接

  • 数据库连接的事情,交给引擎
from sqlalchemy import create_engine
dialect+driver://username:password@host:port/database

#mysqldb的连接
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
engine = sqlalchemy.create_engine("mysql+mysqldb://lqx:lqx@127.0.0.1:3306/hello")

#pymysql的连接
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
engine = sqlalchemy.create_engine("mysql+pymysql://lqx:lqx@127.0.0.1:3306/hello")
engine = sqlalchemy.create_engine("mysql+pymysql://lqx:lqx@127.0.0.1:3306/hello", echo=True)
  • echo=True
    所有的操作都输入到日志。引擎是否打印执行的语句,调试的时候打开很方便

  • lazy connecting
    懒连接。创建引擎并不会马上连接数据库,直到让数据库执行任务时才连接

import sqlalchemy
from sqlalchemy import create_engine

# mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306

engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True)

print(engine)

# 执行结果
Engine(mysql+pymysql://lqx:***@192.168.1.6:3306/test)

Declare a Mapping创建映射

创建基类

from sqlalchemy.ext.declarative import declarative_base
# 创建基类,便于实体类继承。SQLAlchemy大量使用了元编程
Base = declarative_base()

创建实体类

  • student表
CREATE TABLE student (
	id INTEGER NOT NULL AUTO_INCREMENT,
	name VARCHAR(64) NOT NULL,
	age INTEGER,
	PRIMARY KEY (id)
)
  • 做关系映射
import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base

# ORM Mapping
Base = declarative_base() # 基类

class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)
    # 第一个参数是字段名,如果和属性名不一致,一定要指定
    # age = Column('age', Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )

# 查看表结构
print(Student)
print(repr(Student.__table__))

# 显示结果
<class '__main__.Student'>
Table('student', MetaData(bind=None), 
  Column('id', Integer(), table=<student>, primary_key=True, nullable=False),  
  Column('name', String(length=64), table=<student>, nullable=False), 
  Column('age', Integer(), table=<student>), 
  schema=None)

__tablename__指定表名
Column类指定对应的字段,必须指定

实例化

s = Student(name='tom')
print(s.name) # tom
s.age= 20
print(s.age) # 20 

创建表

  • 可以使用SQLAlchemy来创建、删除表
  1. 删除继承自Base的所有表
    Base.metadata.drop_all(engine)
  2. 创建继承自Base的所有表
    Base.metadata.create_all(engine)
import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

print(engine)


# ORM Mapping
Base = declarative_base() # 基类

class Student(Base):
    # 指定表名
    __tablename__= 'student'

    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )

student = Student(id=1, name='jerry')
student.name = 'tom'
student.age= 20

print(student)

生产环境很少这样创建表,都是系统上线的时候由脚本生成
生成环境很少删除表,宁可废弃都不能删除

创建回话session

在一个会话中操作数据库,会话建立在连接上,连接被引擎管理。
当第一次使用数据库时,从引擎维护的连接池中获取一个连接使用.

from sqlalchemy.orm import sessionmaker

# 创建session
Session = sessionmaker(bind=engine) # 工厂方法返回类
session = Session() # 实例化
# 依然在第一次使用时连接数据库

session对象线程不安全。所以不同线程应该使用不用的session对象。
Session类和engine有一个就行了

CRUD操作

增
  • add():增加一个对象
  • add_all():可迭代对象,元素是对象
import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

# ORM Mapping
Base = declarative_base() # 基类

# 创建实体类
class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )

# 删除表
Base.metadata.drop_all(engine)
# 创建表
Base.metadata.create_all(engine)

# 创建seesion
Session = sessionmaker(bind=engine)
session = Session()

s = Student(name='tom') # 构造时传入
s.age = 20 # 属性赋值
print(s)

session.add(s)
print(s)
session.commit()
print(s)
print('~~~~~~')

try:
    session.add_all([s])
    print(s)
    session.commit() # 能提交成功吗?
    print(s)
except:
    session.rollbake()
    print('roll back')
    raise

在这里插入图片描述
add_all()方法不会提交成功的,不是因为它不对,而是s,s成功提交后,s的主键就有了值,所以,只要s没有修改过,就认为没有改动。如下,s变化了,就可以提交修改了

s.name = 'jerry' # 修改
session.add_all([s])

s主键没有值,就是新增;主键有值,就是找到主键对应的记录修改

简单查询 query()

使用query()方法,返回一个Query对象

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

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

# ORM Mapping
Base = declarative_base() # 基类

# 创建实体类
class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )

# # 删除表
# Base.metadata.drop_all(engine)
# 创建表
# Base.metadata.create_all(engine)

# 创建seesion
Session = sessionmaker(bind=engine)
session = Session()

students = session.query(Student)
print(students.count())
for student in students:
    print(student)
print('~~~~~~~~~~~')

student = session.query(Student).get(1) # 通过主键查询
print(student)

在这里插入图片描述
query方法将实体类传入,返回类的对象可迭代对象,这时候并不查询。迭代它就执行SQL来查询数据库,封装数据到指定类的实例
get方法使用主键查询,返回一条传入类的一个实例

修改
  • 修改需先查询,在修改,不然会调用插入方法
import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

# ORM Mapping
Base = declarative_base() # 基类

# 创建实体类
class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )

# # 删除表
# Base.metadata.drop_all(engine)
# 创建表
# Base.metadata.create_all(engine)

# 创建seesion
Session = sessionmaker(bind=engine)
session = Session()

student = session.query(Student).get(1) # 通过主键查询
print(student)
student.name = 'ben'
student.age = 30
print(student)
session.add(student)
session.commit()

在这里插入图片描述

  • 修改前
    在这里插入图片描述
  • 修改后

在这里插入图片描述

删除

先看下数据库,表中有

1 ben 30

编写如下程序来删除,会发生什么?

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

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

# ORM Mapping
Base = declarative_base() # 基类

# 创建实体类
class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )


Session = sessionmaker(bind=engine)
session = Session()

try:
    student = Student(id=1, name='ben', age=30)

    session.delete(student)
    session.commit()
except Exception as e:
    session.rollback()
    print('roll back')
    print(e)

# 执行结果
roll back
Instance '<Student at 0x1779fea9b70>' is not persisted

会产生一个异常
Instance '<Student at 0x3e654e0>' is not persisted 未持久的异常!

状态**

需导入from sqlalchemy.orm.state import InstanceState库

  • 每一个实体,都有一个状态属性_sa_instance_state,其类型是sqlalchemy.orm.state.InstanceState
  • 使用sqlalchemy.inspect(entity)函数查看状态
    常见的状态值有transient、pending、persistent、deleted、detached
状态说明
transient实体类尚未加入到session中,同时并没有保存到数据库中
pendingtransient的实体被add()到session中,状态切换到pending,但它还没有flush到数据库中
persistentsession中的实体对象对应着数据库中的真实记录。pending状态在提交成功后可以变成persistent状态,或者查询成功返回的实体也是persistent状态
deleted实体被删除且已经flush但未commit完成。事务提交成功了,实体变成detached,事务失败,返回persistent状态
detached删除成功的实体进入这个状态

新建一个实体,状态是transient临时的
一旦add()后从transient变成pending状态
成功commit()后从pending变成persistent状态
成功查询返回的实体对象,也是persistent状态

persistent状态的实体,修改依然是persistent状态
persistent状态的实体,删除后,flush但没有commit,就变成deteled状态
成功提交,变为detached状态提交失败,还原到persistent状态。flush方法,主动把改变应用到数据库中去

删除、修改操作,需要对应一个真实的记录,所以要求实体对象是persistent状态

import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.state import InstanceState

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

# ORM Mapping
Base = declarative_base() # 基类

# 创建实体类
class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )


Session = sessionmaker(bind=engine)
session = Session()

def getstate(instance, i):
    inp:InstanceState = sqlalchemy.inspect(student)
    states = "{}: key={}\nid={}, attached={}, transient={}," \
             "pending={}, \npersistant={}, deleted={}, detached={}".format(
        i ,inp.key,
        inp.session_id, inp._attached, inp.transient,
        inp.pending, inp.persistent, inp.deleted, inp.detached
    )
    print(states, end='\n~~~~~~~~~\n')

student = session.query(Student).get(1)
getstate(student, 1) # persistent

try:
    student = Student(id=1, name='ben', age=30)
    getstate(student, 2) # transit

    student = Student(name='tom', age=20)
    getstate(student, 3) # transit
    session.add(student) # add后变成pending
    getstate(student,4) # pending
    # session.delete(student) # 异常,删除的前提必须是persistent,也就是说先查后删
    session.commit() # 提交后,变成persistent
    getstate(student, 6) # persistent
except Exception as e:
    session.rollback()
    print('roll back')

运行结果

1: key=(<class '__main__.Student'>, (1,), None)
id=1, attached=True, transient=False,pending=False, 
persistant=True, deleted=False, detached=False
persistent就是key不为None,附加的,且不是删除的,有sessionid
~~~~~~~~~
2: key=None
id=None, attached=False, transient=True,pending=False, 
persistant=False, deleted=False, detached=False
transient的key为None,且无附加
~~~~~~~~~
3: key=None
id=None, attached=False, transient=True,pending=False, 
persistant=False, deleted=False, detached=False
同上
~~~~~~~~~
4: key=None
id=1, attached=True, transient=False,pending=True, 
persistant=False, deleted=False, detached=False
add后变成pending,已附加,但是没有key,有了sessionid
~~~~~~~~~
6: key=(<class '__main__.Student'>, (2,), None)
id=1, attached=True, transient=False,pending=False, 
persistant=True, deleted=False, detached=False
提交成功后,变成persistent,有了key
~~~~~~~~~
Process finished with exit code 0
  • 删除状态的变化
import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.state import InstanceState

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True) # lazy 懒

# ORM Mapping
Base = declarative_base() # 基类

# 创建实体类
class Student(Base):
    # 指定表名
    __tablename__= 'student'
    # 定义类属性对应字段
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return "<{} id={}, name={}, age={}>".format(
            __class__.__name__, self.id, self.name, self.age
        )


Session = sessionmaker(bind=engine)
session = Session()

def getstate(instance, i):
    inp:InstanceState = sqlalchemy.inspect(student)
    states = "{}: key={}\nid={}, attached={}, transient={}," \
             "pending={}, \npersistant={}, deleted={}, detached={}".format(
        i ,inp.key,
        inp.session_id, inp._attached, inp.transient,
        inp.pending, inp.persistent, inp.deleted, inp.detached
    )
    print(states, end='\n~~~~~~~~~\n')

student = session.query(Student).get(1)
getstate(student, 7) # persistent

try:
    session.delete(student) # 删除的前提是persistent

    getstate(student, 8) # persistent
    session.flush()
    getstate(student, 9) # deleted
    session.commit()
    getstate(student, 13) # detached
except Exception as e:
    session.rollback()
    print('roll back')

执行结果

7: key=(<class '__main__.Student'>, (1,), None)
id=1, attached=True, transient=False,pending=False, 
persistant=True, deleted=False, detached=False
~~~~~~~~~
8: key=(<class '__main__.Student'>, (1,), None)
id=1, attached=True, transient=False,pending=False, 
persistant=True, deleted=False, detached=False
~~~~~~~~~
2019-06-27 16:49:04,554 INFO sqlalchemy.engine.base.Engine DELETE FROM student WHERE student.id = %(id)s
2019-06-27 16:49:04,554 INFO sqlalchemy.engine.base.Engine {'id': 1}
9: key=(<class '__main__.Student'>, (1,), None)
id=1, attached=True, transient=False,pending=False, 
persistant=False, deleted=True, detached=False
delete后flush,状态变成deleted,不过是附加的
~~~~~~~~~

2019-06-27 16:49:04,556 INFO sqlalchemy.engine.base.Engine COMMIT
一旦提交后
13: key=(<class '__main__.Student'>, (1,), None)
id=None, attached=False, transient=False,pending=False, 
persistant=False, deleted=False, detached=True
状态转为detached
~~~~~~~~~
复杂查询 filter

实体类

import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer, Date, Enum, ForeignKey
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.state import InstanceState
import enum

Base = declarative_base() # 基类

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True)

Session = sessionmaker(bind=engine)
session = Session()

class MyEnum(enum.Enum):
    M = 'M'
    F = 'F'


class Employee(Base):
    # 指定表名
    __tablename__ = 'employees'

    # 定义类属性对应字段
    emp_no = Column(Integer, primary_key=True)
    birth_date = Column(Date, nullable=False)
    first_name = Column(String(14), nullable=False)
    last_name = Column(String(16), nullable=False)
    gender = Column(Enum(MyEnum), nullable=False)
    hire_date = Column(Date, nullable=False)

    def __repr__(self):
        return "<{} no={}, name={}, gender={}>".format(
            __class__.__name__, self.emp_no, "{} {}".format(
                self.first_name, self.last_name), self.gender.value
        )

# 打印函数
def show(emps):
    for x in emps:
        print(x)
    print('~~~~~~~~~~~~\n')

# 简单条件查询
emps = session.query(Employee).filter(Employee.emp_no > 10015)
show(emps)
与或非

需导入from sqlalchemy import or_, and_, not_

  • 查询 与 and 四种方式
emps = session.query(Employee).filter(Employee.emp_no > 10015, Employee.emp_no < 10018)
show(emps)

emps = session.query(Employee).filter(emps.emp_no > 10015).filter(Employee.emp_no < 10018)
show(emps)

emps = session.query(Employee).filter(and_(Employee.emp_no > 10015, Employee.emp_no <10018))
show(emps)

emps = session.query(Employee).filter((Employee.emp_no > 10015) & (Employee.emp_no < 10018))
show(emps)
  • 查询 或 or 两种方法
emps = session.query(Employee).filter(or_(Employee.emp_no > 10015, Employee.emp_no < 10018))
show(emps)

emps = session.query(Employee).filter((Employee.emp_no > 10015) | (Employee.emp_no < 10018))
show(emps)
  • 查询 非 两种方法
emps = session.query(Employee).filter(not_(Employee.emp_no < 10018))
show(emps)

emps = session.query(Employee).filter(~(Employee.emp_no > 10018))
show(emps)
  • in 操作
emps = session.query(Employee).filter(Employee.emp_no.in_([10015, 10018, 10020]))
show(emps)
  • not in 操作
emps = session.query(Employee).filter(~Employee.emp_no.in_([10015, 10018, 10020]))
show(emps)

emps = session.query(Employee).filter(~Employee.emp_no.notin_([10015, 10018, 10020]))
show(emps)
  • like 字符串匹配操作
emps = session.query(Employee).filter(Employee.last_name.like('p%'))
show(emps)
  • not like
emps = session.query(Employee).filter(Employee.last_name.notlike('p%'))
show(emps)

ilike可以忽略带小写匹配

排序 order_by
  • 升序
emps = session.query(Employee).filter(Employee.emp_no > 10010).order_by(Employee.emp_no)
emps = session.query(Employee).filter(Employee.emp_no > 10010).order_by(Employee.emp_no.asc())
show(emps)
  • 降序
emps = session.query(Employee).filter(Employee.emp_no > 10010).order_by(Employee.emp_no.desc())
show(emps)
  • 多列排序
emps = session.query(Employee).filter(Employee.emp_no >
    10010).order_by(Employee.last_name).order_by(Employee.emp_no.desc())
show(emps)
分页 limit
emps = session.query(Employee).limit(4)
show(emps)

emps = session.query(Employee).limit(4).offset(18)
show(emps)
消费者方法

消费者方法调用后,Query对象(可迭代)就转换成了一个容器

# 总行数
emps = session.query(Employee)
print(emps.count()) # 聚合函数count(*)的查询

# 取所有数据
print(emps.all()) # 返回列表,查不到返回空列表

# 取首行
print(emps.first()) # 返回首行,查不到返回None,等价limit

# 有且只能有一行
print(emps.one()) #如果查询结果是多行抛异常
print(emps.limit(1).one())

# 删除 delete by query
session.query(Employee).filter(Employee.emp_no > 10018).delete()
session.commmit # 提交则删除
聚合、分组

需导入 from sqlalchemy import func

  • 聚合函数
# count
from sqlalchemy import func

query = session.query(func.count(Employee.emp_no)) 
print(query.all()) # 列表中一个元素
print(query.first) # 一个只有一个元组的元组
print(query.one()) # 只能有一行返回,一个元组
print(query.scalar()) # 取one()的第一个元素

# max/min/avg
print(session.query(func.max(Employee.emp_no)).scalar())
print(session.query(func.min(Employee.emp_no)).scalar())
print(session.query(func.avg(Employee.emp_no)).scalar())
  • 分组
query = session.query(Employee.gender,
    func.count(Employee.emp_no)).group_by(Employee.gender).all()
for g,y in query:
    print(g.value, y)
关联查询

在这里插入图片描述
从语句看出员工、部门之间的关系是多对多关系。
先把这些表的Model类和字段属性建立起来。

import sqlalchemy
from sqlalchemy import create_engine, Column, String, Integer, Date, Enum, ForeignKey
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.state import InstanceState
from sqlalchemy import or_, and_, not_
import enum
from sqlalchemy import func

Base = declarative_base() # 基类

IP = '192.168.1.6'
USERNAME = 'lqx'
PASSWORD = 'lqx'
DBNAME ='test'
PORT = 3306
engine = create_engine("mysql+pymysql://{}:{}@{}:{}/{}".format(
    USERNAME, PASSWORD, IP, PORT, DBNAME
), echo=True)

Session = sessionmaker(bind=engine)
session = Session()

class MyEnum(enum.Enum):
    M = 'M'
    F = 'F'
    
# 打印函数
def show(emps):
    for x in emps:
        print(x)
    print('~~~~~~~~~~~~\n')

class Employee(Base):
    # 指定表名
    __tablename__ = 'employees'

    # 定义类属性对应字段
    emp_no = Column(Integer, primary_key=True)
    birth_date = Column(Date, nullable=False)
    first_name = Column(String(14), nullable=False)
    last_name = Column(String(16), nullable=False)
    gender = Column(Enum(MyEnum), nullable=False)
    hire_date = Column(Date, nullable=False)

    def __repr__(self):
        return "<{} no={}, name={}, gender={}>".format(
            __class__.__name__, self.emp_no, "{} {}".format(
                self.first_name, self.last_name), self.gender.value
        )

class Department(Base):
    __tablename__ = 'departments'

    dept_no = Column(String(4), primary_key=True)
    dept_name = Column(String(40), nullable=False, unique=True)

    def __repr__(self):
        return "{} no={} name={}".format(
            type(self).__name__, self.dept_no, self.dept_name)

class Dept_emp(Base):
    __tablename__ = 'dept_emp'

    emp_no = Column(Integer, ForeignKey('employees.emp_no',
                    ondelete='CASCADE'), primary_key=True)
    dept_no = Column(String(4), ForeignKey('depatments.dept_no', ondelete='CASCADE'),
                     primary_key=True)
    from_date = Column(Date, nullable=False)
    to_date = Column(Date, nullable=False)

    def __repr__(self):
        return "{} empno={} deptno={}".format(
            type(self).__name__, self.emp_no, self.dept_no)

ForeignKey('employees.emp_no', ondelete='CASCADE')定义外键约束

需求:查询10010员工的所在的部门编号及员工信息

  • 1、使用隐式内连接
# 查询10010员工的所在的部门编号及员工信息
results = session.query(Employee, Dept_emp).filter(
    Employee.emp_no == Dept_emp.emp_no).filter(Employee.emp_no == 10010).all()

show(results)

# 查询结果2行
(<Employee no=10010, name=Duangkaew Piveteau, gender=F>, Dept_emp empno=10010 deptno=d004)
(<Employee no=10010, name=Duangkaew Piveteau, gender=F>, Dept_emp empno=10010 deptno=d006)

这种方式会产生隐式连接的语句

SELECT * 
FROM employees, dept_emp 
WHERE employees.emp_no = dept_emp.emp_no AND employees.emp_no = 10010
  1. 使用join
# 查询10010员工的所在的部门编号及员工信息
# 第一种写法
results = session.query(Employee).join(Dept_emp).filter(Employee.emp_no == 10010).all()

第二种写法
results = session.query(Employee).join(Dept_emp,
            Employee.emp_no == Dept_emp.emp_no).filter(Employee.emp_no == 10010).all()

show(results)

这两种写法,返回都只有一行数据,为什么?

  • 它们生成的SQL语句是一样的,执行该SQL语句返回确实是2行记录,可是Python中的返回值列表中只有一个元素?
  • 原因在于 query(Employee) 这个只能返回一个实体对象中去,为了解决这个问题,需要修改实体类Employee,增加属性用来存放部门信息

sqlalchemy.orm.relationship(实体类名字符串)
需导入from sqlalchemy.orm import sessionmaker,relationship

from sqlalchemy.orm import sessionmaker,relationship

class Employee(Base):
    # 指定表名
    __tablename__ = 'employees'

    # 定义类属性对应字段
    emp_no = Column(Integer, primary_key=True)
    birth_date = Column(Date, nullable=False)
    first_name = Column(String(14), nullable=False)
    last_name = Column(String(16), nullable=False)
    gender = Column(Enum(MyEnum), nullable=False)
    hire_date = Column(Date, nullable=False)

    departmens = relationship('Dept_emp') #

    def __repr__(self): # 注意增加self.dept_emps
        return "<{} no={}, name={}, gender={} depts={}>".format(
            __class__.__name__, self.emp_no, "{} {}".format(
            self.first_name, self.last_name), self.gender.value,
            self.departmens
        )
  • 查询信息
# 查询10010员工的所在的部门编号及员工信息
# 第一种
results = session.query(Employee).join(Dept_emp).filter(
            Employee.emp_no == Dept_emp.emp_no).filter(Employee.emp_no == 10010)

# 第二种
results = session.query(Employee).join(Dept_emp,
            Employee.emp_no == Dept_emp.emp_no).fiter(Employee.emp_no == 10010)

# 第三种
results = session.query(Employee).join(Dept_emp, 
            (Employee.emp_no == Dept_emp.emp_no) & (Employee.emp_no == 10010))
show(results.all()) # 打印结果
  • 第一种方法join(Dept_emp)中没有等值条件,会自动生成一个等值条件,如果后面有filter,哪怕是filter(Employee.emp_no == Dept_emp.emp_no),这个条件会在where中出现。第一种这种自动增加join的等值条件的方式不好,不要这么写
  • 第二种方法在join中增加等值条件,阻止了自动的等值条件的生成。这种方式推荐
  • 第三种方法就是第二种,这种方式也可以

再看一个现象

results = session.query(Employee).join(Dept_emp).filter(
            Employee.emp_no == Dept_emp.emp_no).filter(Employee.emp_no == 10010)

for x in results:
    print(x.emp_no)

可以看出只要不访问departments属性,就不会查dept_emp这张表

总结

在开发中,一般都会采用ORM框架,这样就可以使用对象操作表了
定义表映射的类,使用Column的描述器定义类属性,使用ForeignKey来定义外键约束
如果在一个对象中,想查看其它表对应的对象的内容,就要使用relationship来定义关系

是否使用外键约束?

  1. 力挺派
    能使数据保证完整性一致性
  2. 弃用派
    开发难度增加,大量数据的时候影响插入、修改、删除的效率。
    在业务层保证数据的一致性。
【项目实战】Python数据存储和数据库中常用的库(SQLAlchemy/Peewee/Django ORM/PyMongo/Redis - py/Cassandra - driver) 阅读详情

相关推荐

sqlalchemy:适用于Python的数据库工具包

sqlalchemy:适用于Python的数据库工具包

python常用库之数据库orm框架之SQLAlchemy

SQLAlchemy是一个基于Python实现的SQL工具包和ORM框架,提供了高层抽象来管理数据库交互。

西京刀客 2万+

一步步指导:在Chrome中安装Vue插件vue-devtools

Vue.js是现代Web开发中最流行的前端框架之一,它以其轻量级、高性能和易于理解的设计理念受到开发者的喜爱。为了更高效地开发和调试Vue应用,Vue.js官方提供了一个强大的开发工具——vue-devtools。

小透明的博客 5618

详细分析Python中的SQLAlchemy库(附Demo)

SQLAlchemy 是Python SQL工具包和对象关系映射器(ORM),允许Python开发者在应用程序中使用SQL来交互,而无需处理数据库的具体细节 提供一个高层的抽象层,允许开发者通过Python类和对象来表示数据库中的表和行,从而使得数据库操作更加方便和灵活

码农研究僧的博客 6557

STM32----SPI+DMA高效读写外部Flash的配置与实战

本文详细介绍了STM32通过SPI+DMA高效读写外部Flash的配置与实战方法。针对大量数据传输场景,使用DMA技术可显著提升系统效率,释放CPU资源。文章包含完整的代码实现、常见问题调试技巧以及性能优化建议,帮助开发者快速掌握这一关键技术。

pea55的博客 1030

USVInland 数据集实战:4个月采集27段数据,26公里内河SLAM评测

本文详细解析了USVInland数据集在内河SLAM算法评测中的应用实践。通过4个月采集的27段共26公里真实多传感器数据,该数据集有效解决了GPS信号遮挡、动态反射干扰等内河无人船SLAM核心挑战。文章从数据预处理、算法优化到性能评测,全面展示了如何利用毫米波雷达与视觉数据的时空对齐特性提升定位精度,为水上无人驾驶技术提供重要基准。

weixin_33158887的博客 341

SQLAlchemy 介绍与实践

SQLAlchemy 是一个 ORM 框架。SQLAlchemy 是一个用于 Python 的 SQL 工具和对象关系映射(ORM)库。它允许你通过 Python 代码来与关系型数据库交互,而不必直接编写SQL语句。简单介绍一下对象关系映射吧,对象关系映射(英语:Object Relational Mapping,简称 ORM,或O/RM,或O/R mapping),是一种程序设计技术, 用于实现面向对象编程语言里不同类型系统的数据之间的转换。

cliffordl的专栏 4826

BLE天线设计避坑:NRF52832官方50欧姆阻抗匹配的3个常见误区

本文深入解析NRF52832蓝牙SoC天线设计中的常见误区,重点探讨50欧姆阻抗匹配的3个关键问题:PCB板材介电常数差异、匹配元件寄生参数及VSWR测试。通过实测数据和优化方案,帮助工程师规避设计陷阱,提升BLE通信性能。

beer8的博客 214

Python虚拟化环境下载sqlalchemy

1、进入虚拟化环境: venv\Scripts\activate C:\Users\Administrator.SC-201808271616>venv\Scripts\activate (venv) C:\Users\Administrator.SC-201808271616> 2、安装前检测: pip3 freeze (venv) C:\Users\Administrator.SC-201808271616>pip3 freeze click==7.1.2 Flask==1.1.2

鲸落的博客 838

银河麒麟系统完美安装EasyConnect

Kylin V10桌面版完美安装EasyConnect

weixin_53874797的博客 1万+

【人工智能】Claude Code 安装、配置及问题排查完整指南

建议使用IDE来启动 Claude,执行后,内容如下。(确保环境可正常运行 npm 命令)。

奶爸的编程之路,也就一周冷个三天 1206

python oracle orm_Python库之SQLAlchemy

一、SQLAlchemy简介1.1、SQLAlchemy是什么?sqlalchemy是一个python语言实现的的针对关系型数据库的orm库。可用于连接大多数常见的数据库,比如Postges、MySQL、SQLite、Oracle等。1.2、为什么要使用SQLAlchemy?它将你的代码从底层数据库及其相关的SQL特性中抽象出来。1.3、SQLAlchemy提供了两种主要的使用模式SQL表达式语言...

weixin_39900736的博客 352

代码解读:使用Stable Diffusion完成相似图像生成任务

作为内容生产重要的一部分,生成相似图像是一项有意义的工作,例如很多内容创作分享平台单纯依赖用户贡献的图片已经不够了,最省力的方法就是利用已有的图片生成相似的图片作为补充。这篇博客详细解读基于Stable Diffusion生成相似图片的原理和代码。

沉迷单车的追风少年 1392

Python库学习(十四):ORM框架-SQLAlchemy

SQLAlchemy是一个用于Python的SQL工具和对象关系映射ORM)库。它允许开发者通过Python代码而不是SQL查询语言来操作数据库。SQLAlchemy提供了一种灵活且强大的方式来与关系型数据库交互,支持多种数据库后端,如等。本文使用的SQLAlchemy版本: 1.4.51# 模型父类# 用户模型和表一一对应__table_args__ = {'comment': '用户表'}id = Column(BIGINT, primary_key=True, comment='主键')

很多时候犯错都是在不知情的情况下发生的 1248

ExpressLRS(ELRS)接收机技术全景:从历史演进、技术底层到产业生态全解析——上

ExpressLRS(ELRS)是一种由全球开源社区驱动的双向射频遥控链路协议栈,其接收机作为核心终端,承担信号接收解调、遥控指令解析、飞控数据交互与遥测回传等关键功能。自2018年问世以来,ELRS凭借极致性能、低成本、开放生态和强兼容性,从航模DIY方案发展为消费级无人机领域的事实标准,并逐步渗透工业级场景。 ELRS接收机采用模块化分层架构,支持2.4GHz和900MHz双频段,通过LoRa、FLRC、FSK多调制模式实现低延迟与远距离平衡。其核心技术包括自适应跳频抗干扰、TDD双向链路和极简数据包。

bother3000的博客 302

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

python录入数据_python通过sqlalchemy库操作mysql写入数据[ORM]

ORM是什么,我这里就不说啦,相信在看的都应该已经清楚了,这里我直接上使用方法。首先python自带的库是不支持orm的,所以这里我们需要安装sqlalchemy库进行支持,通过pip就可以安装了pip install sqlalchemypip install pymysql如果pymysql已经安装就不用装了,如果是使用其它类库连接mysql数据库,sqlalchemy也是支持的这里是演示示例...

weixin_39541600的博客 357

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Python第三方ORM库:SQLAlchemy简介

Python ORM之SQLAlchemy

OneFine的技术博客 1448

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

python SQLAlchemy ORM——从零开始学习 01 安装库

通过create_engine接口创建engine,根据翻译可以翻译成引擎,和发动机一样,有了这个才能驱动数据库启动,但创建engine的接口接受的是一个针对 PostgreSQL的对象,对象的示例如:postgresql+psycopg2://scott:tiger@localhost:5432/mydatabase。第二个使用到的是primary_key,即数据库的主键(数据库中数值唯一的一种属性,一般是数据库自动添加的,例如作为整形id,如果主键为TURE,则id这个属性的数值不会重复)

weixin_55010563的博客 623

Python学习之 --- 数据库编程之 ORM+sqlalchemy 库使用

ORM ORM,对象关系映射,对象和关系之间的映射,使用面向对象的方式来操作数据库。 关系模型和Python对象之间的映射 table => class ,表映射为类 row => object ,行映射为实例 column => property ,字段映射为属性 SQLAlchemy SQLAlchemy是一个ORM框架 安装 pip install sqlalchemy...

等风来的博客 600

python库使用方法查询_Python的ORM框架中SQLAlchemy库的查询操作的教程

1. 返回列表和标量(Scalar)前面我们注意到Query对象可以返回可迭代的值(iterator value),然后我们可以通过for in来查询。不过Query对象的all()、one()以及first()方法将返回非迭代值(non-iterator value),比如说all()返回的是一个列表:>>> query = session.query(User).\>&...

weixin_35827671的博客 368

python建库_Python数据库ORM工具sqlalchemy的学习笔记

SQLAlchemy是python的一个数据库ORM工具,提供了强大的对象模型间的转换,可以满足绝大多数数据库操作的需求,并且支持多种数据库引擎(sqlite,mysql,postgres, mongodb等),在这里记录基本用法和学习笔记。一、安装通过pip安装 $pipinstallSQLAlchemy二、使用首先是连接到数据库,SQLALchemy支持多个数据库引擎,不同的数据...

weixin_39560924的博客 339

python 映射关系_研究Python的ORM框架中的SQLAlchemy库的映射关系

研究Python的ORM框架中的SQLAlchemy库的映射关系来源:中文源码网浏览: 次日期:2018年9月2日研究Python的ORM框架中的SQLAlchemy库的映射关系 前面介绍了关于用户账户的User表,但是现实生活中随着问题的复杂化数据库存储的数据不可能这么简单,让我们设想有另外一张表,这张表和User有联系,也能够被映射和查询,那么这张表可以存...

weixin_30650955的博客 397

python ORM链接数据库-sqlalchemy库的使用(一)

最近上线的任务有点多,一直想写的东西也没啥时间,赶上周六在公司附近体检顺道就来公司写的东西。今天就简单写一个python连接数据库的第三方库sqlalchemy一点点自己的使用心得。 1.说说接口测试为什么要连接数据库,大体上分为两部分,第一部分就是request和response,请求发送给服务端,服务端收到请求后给我们对应的response(数据类型是字典格式),其中请求中的一些参数当然可以

gogoboi_jin的博客 1327

数据存储 | MySQL —— ORM框架的使用(python:sqlalchemy库)

1.参考博客 https://www.cnblogs.com/wupeiqi/articles/5713330.html 2.实战代码 from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy import create_engine from

也许有一天我们再相逢 睁开眼睛看清楚 我才是英雄! 385

python操作数据库 SQLAlchemy库与orm操作(创建表与添加数据操作 简单操作)

通过python操作数据库的方式可以是SQLAlchemy, 具体代码如下: from sqlalchemy import create_engine # 创造一个引擎 与mysql相连接 # 连接数据库 # 主机地址 HOSTNAME = '127.0.0.1' # 数据库名称 DATABASE = 'flask_demo' # 端口 PORT = 3306 # 用户名和密码 USERNAME = 'root' PASSWORD = 'root' DB_URL = 'mysql+pymysql:

LCY133的博客 1652
上一篇: Python-- 数据库开发(pymysql)
下一篇: Python -- 元编程
鹿夏
博客等级 码龄8年 24粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值