SQLAlchemy

1.介绍

  • SQLAlchemy以其对象关系映射器(ORM)而闻名,它是一个提供数据映射器模式的可选组件,其中类可以以开放式、多种方式映射到数据库——允许对象模型和数据库模式在一个从一开始就干净地解耦。简单来讲只需要用python的语法来操作对象,就能被自动映射为sql语句。
  • sqlalchemy第三方orm框架(对象关系映射),可以单独使用。
  • 底层依附于pymysql

2.安装

pip install sqlalchemy

3.组成部分

Engine框架的引擎
Connection Pooling数据库连接池
Dialect选择连接数据库的DB API类
Schema/Types架构和类型
SQL Exprression LanguageSQL表达式语言

SQLAlchemy本身无法操作数据库,其必须依赖pymysql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API从而实现对数据库的操作




4.基本使用

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine

"""
比pymysql优秀在哪里
自己维护了一个池

"""

# 第一步 生成一个engine对象
engine = create_engine(
    'mysql+pymysql://root:123456@127.0.0.1:3306/django?charset=utf8',
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的事件,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步:创建连接(执行原生sql)
conn = engine.raw_connection()
# 第三步:获取游标对象
cursor = conn.cursor()

# 第四步:具体操作
cursor.execute('select * from app01_user')
res = cursor.fetchall()
print(res)



5.orm



5.1 各种字段类型创建

只能通过base创建表,不能创建库,不能修改表
以下只为创建实例使用,不与下文增删改查关联

import datetime
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index

# 制造了一个类,作为所有模型类的基类
Base = declarative_base()


# 创建一个个的类,继承Base
class User(Base):
    __tablename__ = 'users'  # 数据库表名称
    id = Column(Integer, primary_key=True)  # id 主键
    name = Column(String(32), index=True, nullable=False)  # name 索引,不为空
    email = Column(String(32), unique=True)  # unique唯一
    ctime = Column(DateTime, default=datetime.datetime.now)
    extra = Column(Text, nullable=True)

    # 类似于django中的Meta
    __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),  # 联合唯一
        Index('ix_id_name', 'name', 'email')  # 索引
    )


5.2 base管理表创建和删除

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base


# 创建表
# 创建的表都是通过base管理的表
def create_table():
    # 创建对象,通过engine_create创建出表
    engine = create_engine(
        'mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的事件,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.create_all(engine)


# 删除表
def drop_table():
    engine = create_engine(
        'mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的事件,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    # 通过engine删除所有的表(通过Base管理的表)
    Base.metadata.drop_all(engine)


if __name__ == '__main__':
	# 创建表
    create_table()
    # 删除表
    # drop_table()

5.3 orm操作

user表创建

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

# 制造了一个类,作为所有模型类的基类
Base = declarative_base()

# 创建一个个的类(继承谁?字段怎么写)
class Name(Base):
    __tablename__ = 'name'  # 数据库表名称
    id = Column(Integer, primary_key=True)  # id 主键
    name = Column(String(32), index=True, nullable=False)  # name 索引,不为空

表创建(通过复制5.2中的内容执行)完成后执行以下操作

基本操作

from models import Name
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

# 第一步 生成一个engine对象
engine = create_engine(
    'mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8',
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的事件,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步 制造一个session类
Session = sessionmaker(bind=engine)
# 第三步 得到一个session对象
session = Session()
# 第四步 创建一个对象
obj1 = Name(name='zyc')
# 第五步 把对象放进session对象中
session.add(obj1)
# 第六步 提交
session.commit()
session.close()



6.线程安全

from models import User
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import create_engine

# 第一步 生成一个engine对象
engine = create_engine(
    'mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8',
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的事件,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步 制造一个session类
Session = sessionmaker(bind=engine)
# 第三步 得到一个session对象(线程安全的session对象)
'''
不再使用Session对象,通过scoped_session将Session传入,
内部实现通过threading.local()实现,从而保证了数据的安全。
'''
session = scoped_session(Session)
# 第四步 创建一个对象
obj1 = User(name='lqz')
# 第五步 把对象放进session对象中
session.add(obj1)
# 第六步 提交
session.commit()
session.close()

7.单表增删改查操作

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

# 制造了一个类,作为所有模型类的基类
Base = declarative_base()


# 创建一个个的类(继承谁?字段怎么写)
class User(Base):
    __tablename__ = 'users'  # 数据库表名称
    id = Column(Integer, primary_key=True)  # id 主键
    name = Column(String(32), index=True, nullable=False)  # name 索引,不为空
    def __repr__(self):
        return self.name          

表创建(通过复制5.2中的内容执行)完成后执行以下操作

ps:以下内容为增删改查操作的时候需要的session对象创建

from models import User
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

engine = create_engine(
    'mysql+pymysql://root:123456@127.0.0.1:3306/test?charset=utf8',
    echo=True,  # 打印log信息
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的事件,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Session = sessionmaker(bind=engine)
session = Session()

# your code write here

# 并不是真正关闭数据库,而是将连接关闭,放回池中
session.close()

7.1 增

新增单条数据

obj = User(name='张三')
session.add(obj)
session.commit()

新增多条数据

obj1 = User(name='李四')
obj2 = User(name='王五')
obj3 = User(name='马六')
session.add_all([obj1, obj2, obj3])
session.commit()

7.2 查

'''
    filter              过滤
    filter_by           过滤
    all                 返回全部符合条件的结果
    first               返回全部结果中的第一个结果
    
    filter和filter_by的区别?
    filter:用类名.属性名,比较用==,支持and,or和in等
    filter_by:直接用属性名,比较用=
    最重要的区别是filter不支持组合查询,只能连续调用filter来变相实现。
    而filter_by的参数是**kwargs,直接支持组合查询。
'''
ret = session.query(User).filter(User.id == 4).all()			# list
ret = session.query(User).filter_by(id=4).all()					# list
ret = session.query(User).filter(User.id <= 4).all()			# list
ret = session.query(User).filter(User.id >= 4).first()			# object
print(ret)

# 原生sql查询
res = session.query(User).from_statement(text("SELECT * FROM users where name=:name")).params(name='zzz999').first()
print(res.id)
print(res.name)

7.3 删

ret = session.query(User).filter(User.id==10).delete()
session.commit()

7.4 改

# 固定用法 synchronize_session为False时做拼接,为evaluate时,数字相加
session.query(User).filter(User.id > 0).update({User.name: User.name + '999'}, synchronize_session=False)
session.query(User).filter(User.id > 0).update({User.id: User.id + 1}, synchronize_session='evaluate')
ret = session.query(User).filter_by(id=11).update({User.name: 'ccc'})
ret = session.query(User).filter(User.id == 11).update({User.name: 'xxx'})
session.commit()

PS:操作orm时,增、删、改都需要commit操作,数据才会进行同步。



8.orm其他高级查询


8.1 表达式and条件连接

res = session.query(User).filter(User.id >= 1, User.name == 'lqz').all()
print(res)

8.2 between

res = session.query(User).filter(User.id.between(0, 3), User.name == 'lqz').all()
print(res)

8.3 in条件

res = session.query(User).filter(User.id.in_([1, 3, 4]), User.name == 'lqz').all()
print(res)

8.4 二次筛选

res = session.query(User.name, User.id).filter(User.id.in_(session.query(User.id).filter_by(name='lqz'))).all()
print(res)

8.5与或非

from sqlalchemy import and_, or_

res = session.query(User).filter(and_(User.id >= 2, User.name == 'alex')).all()
res = session.query(User).filter(or_(User.id > 10, User.name == 'alex')).all()
res = session.query(User).filter(~or_(User.id > 10, User.name == 'alex')).all()
print(res)

8.6 通配符 like

# 查询用户名字以a开头的用户
res = session.query(User).filter(User.name.like('a%')).all()
# 查询用户名字不以a开头的用户
res = session.query(User.id).filter(~User.name.like('a%')).all()
print(res)

8.7 限制,用于分页,区间,前闭后开

res = session.query(User)[1:2]
print(res)

8.8 排序

# 排序,desc降序,asc升序
res = session.query(User.id, User.name).order_by(User.id.desc()).all()
res = session.query(User.id, User.name).order_by(User.id.asc()).all()
res = session.query(User.id, User.name).order_by(User.name.desc(), User.id.asc()).all()
print(res)

8.8 分组

from sqlalchemy.sql import func

res = session.query(User).group_by(User.name).all()
res = session.query(func.max(User.id)).group_by(User.name).all()
res = session.query(func.min(User.id)).group_by(User.name).all()
res = session.query(func.sum(User.id)).group_by(User.name).all()
res = session.query(func.count(User.id)).group_by(User.name).all()
res = session.query(func.avg(User.id)).group_by(User.name).all()
print(res)

8.9 having筛选

res = session.query(func.min(User.id), User.name).group_by(User.name).having(func.min(User.id) > 2).all()
print(res)

8.10 组合(UNION)

# UNION操作符用于合并两个或更多的select语句的结果集
# union         并集去重
# union all     并集 不去重

# union
from models import User
q1 = session.query(User.id).filter(User.id > 1)
q2 = session.query(User.id).filter(User.id < 3)
res = q1.union(q2).all()
print(res)

#union all
q1 = session.query(User.id).filter(User.id > 1)
q2 = session.query(User.id).filter(User.id < 3)
res = q1.union_all(q2).all()
print(res)

8.11 执行原生sql

session.execute('insert into boy(hostname) values (:value)', params={'value': '马冬梅'})
session.commit()



9.一对多表操作


9.1 表关系创建

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship

Base = declarative_base()


class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='篮球')


class Person(Base):
    __tablename__ = 'person'
    nid = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=True)
    hobby_id = Column(Integer, ForeignKey('hobby.id'))

    # 跟数据库无关,不会新增字段,只用于快速链表操作
    # 类名,backref用于反向查询
    hobby = relationship('Hobby', backref='pers')

PS:表创建(通过复制5.2中的内容执行)完成后执行以下操作


9.2 插入数据

9.2.1 方式一 (最笨的方法)
h_obj = Hobby(caption='打球')
p_obj = Person(name='王五', hobby_id='2')
session.add_all([h_obj, p_obj])
session.commit()

9.2.2 方式二(默认传入对象不行)
# 默认传入对象不支持,需要在表中添加relationship字段
p = Person(name='马六', hobby=Hobby(caption='就这'))
session.add(p)
session.commit()

# 等同于
p = Person(name='马六2')
p.hobby = Hobby(caption='就这2')
session.add(p)
session.commit()

9.2.3 方式三(反向操作)
hobby = Hobby(caption='做饭')
hobby.pers = [Person(name='张三啊')]	# 可以传入多个对象,列表中逗号隔开
session.add(hobby)
session.commit()

9.3 查询

9.3.1 基于对象的跨表查询
# 正向查询
p = session.query(Person).filter_by(name='张三').first()
print(p)
print(p.hobby.caption)

# 反向查询
h = session.query(Hobby).filter_by(caption='做饭').first()
print(h)
print(h.pers[0].name)

9.3.2 基于连表的查询
'''
    join连表默认通过外键连接
    isouter默认为False,表示inner join
    isouter = True表示左外连接,表示Person left outer join Hobby
    ps:写完查询后,不添加.all或者.first就可以查看原生sql语句,例如
    session.query(Hobby).filter_by(caption='做饭')
    打印出来就是原生sql
'''
# 左外连接
res = session.query(Person).join(Hobby, isouter=True).filter(Hobby.caption == '做饭').first()
res = session.query(Person).join(Hobby, isouter=True).filter_by(caption='做饭').first()	# 和上边相同
print(res)
# 原生sql
'''SELECT * FROM person LEFT OUTER JOIN hobby ON hobby.id = person.hobby_id WHERE hobby.caption = "做饭"'''

# 内连接
res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()
print(res)
res = session.query(Person).join(Hobby).all()
print(res)

# 指定连表字段(基本不用)
res = session.query(Person).join(Hobby, Person.nid == Hobby.id, isouter=True).all()
print(res)



10.多对多表操作


10.1 表关系创建

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, ForeignKey, Integer
from sqlalchemy.orm import relationship

Base = declarative_base()


class Boy(Base):
    __tablename__ = 'boy'
    id = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(64), unique=True, nullable=False)

    # 与生成表结构无关,仅用于查询方便,放在哪个表中都可以
    # secondary通过哪个表监理关联,跟django中的through一模一样
    girls = relationship('Girl', secondary='boy2girl', backref='boys')


class Girl(Base):
    __tablename__ = 'girl'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(64), unique=True, nullable=False)


class Boy2Girl(Base):
    __tablename__ = 'boy2girl'
    id = Column(Integer, primary_key=True, autoincrement=True)
    girl_id = Column(Integer, ForeignKey('girl.id'))
    boy_id = Column(Integer, ForeignKey('boy.id'))

PS:表创建(通过复制5.2中的内容执行)完成后执行以下操作

10.2 插入数据

10.2.1 最原始的方式
session.add_all([
    Boy(hostname='小明'),
    Boy(hostname='小刚'),
    Girl(name='小红'),
    Girl(name='小翠'),
])
session.commit()
session.add_all([
    Boy2Girl(girl_id=1, boy_id=1),
    Boy2Girl(girl_id=2, boy_id=1),
    Boy2Girl(girl_id=2, boy_id=1)
])
session.commit()

10.2.2 前提要有relationship建立
girl = Girl(name='小青')
girl.boys = [Boy(hostname='小强'), Boy(hostname='小丁')]
session.add(girl)
session.commit()


boy = Boy(hostname='小布')
boy.girls = [Girl(name='小白'), Girl(name='小粉')]
session.add(boy)
session.commit()

10.3 查询

10.3.1 基于对象的跨表查询
# 查询和小白认识的所有男生
girl = session.query(Girl).filter_by(name='小白').first()
print(girl.boys)
# 查询和小明认识的所有女生
boy = session.query(Boy).filter_by(hostname='小明').first()
print(boy.girls)

10.3.2 连表
res = session.query(Girl.name).filter(Boy.id == Boy2Girl.boy_id, Girl.id == Boy2Girl.girl_id,
                                      Boy.hostname == '小丁').first()
print(res)

10.3.2 使用join连表查询
res = session.query(Girl.id, Girl.name).join(Boy2Girl).join(Boy).filter(Boy.hostname == '小明').all()
print(res)

res = session.query(Boy.hostname).join(Boy2Girl).join(Girl).filter(Girl.name == '小红').all()
print(res)

欢迎补充

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呀儿呦丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值