公众号:uncle39py

V1

2022/02/04阅读:90主题:默认主题

sqlachemy

sqlachemy:很吻合原生sql的第三方orm框架(非常好用)

sqlachemy做的事情是对象关系映射,他底层仍需借助于数据库驱动来完成数据库连接及cursor.execute(sql)等操作

sqlachemy支持多种数据库,可在官方文档查阅:https://docs.sqlalchemy.org/en/14/dialects/index.html,以下我以mysql+pymysql为例:

连接数据库

安装pip install sqlalchemy

以下是个小例子,可以用于测试是否能够正常连接数据库,使用的时候不会这样用的

# 1.创建一个engine对象,理解成动力源,能量源
engine = create_engine(
    'mysql+pymysql://root:root@127.0.0.1:3306/test?charset=utf8',
    max_overflow=0# 超出连接池后,最多再能创建的连接数
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有连接后最多等待的时间,超过则报错
    pool_recycle=-1  # -1代表不回收
)
# 第一个参数:mysql+pymysql://用户名:密码@host:port/数据库名?charset=utf8
# 后面的4个参数取默认,详细可看官方文档


# 2.由engine来创建连接
conn = engine.raw_connection()  # raw:原始的

#3.获取游标对象
cursor = conn.cursor()
cursor.execute('select * from boy')
res = cursor.fetchall()
print(res)

使用sqlachemy创建表/删除表

# 创建一个一个类(关键点:1.继承谁 2.字段怎么写)

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

#1.创建一个类作为所有模型类的基类
Base = declarative_base()

class User(Base):
    __tablename__='users'  # 数据库的表名
    id = Column(Integer,primary_key=True,autoincrement=True# 主键;自增(默认就是True)
    name = Column(String(32),nullable=False)  # 不为空
    email = Column(String(32),unique=True)  # 唯一
    ctime = Column(DateTime,default=datetime.datetime.now) # 默认值
    extra = Column(Text) # 大文本

#2.创建表
def create_table():
    engine = create_engine(
        'mysql+pymysql://root:root@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) # 通过engine对象创建表,只要是继承Base的类,都会被创建出来表

#3.删除表
def drop_table():
    engine = create_engine(
        'mysql+pymysql://root:root@127.0.0.1:3306/test?charset=utf8',
        max_overflow=0,
        pool_size=5,
        pool_timeout=30,
        pool_recycle=-1
    )
    Base.metadata.drop_all(engine) # 通过engine对象删除表,只要是Base管理的表,都会被删除

if __name__ == '__main__':
    # create_table()
    drop_table()

#存在问题:sqlachemy不支持创建数据库和修改表字段

单表 增删改查

from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy.orm import sessionmaker, scoped_session
from models import User

engine = create_engine(
    'mysql+pymysql://root:root@127.0.0.1:3306/test?charset=utf8',
    max_overflow=0,
    pool_size=5,
    pool_timeout=30,
    pool_recycle=-1
)

#知识点1:线程安全的session
Session = sessionmaker(bind=engine) # 得到一个类
#session = Session() # 得到一个session对象,把session理解成一个临时存放对象的中转仓,所有的增删改查都是操作这个空间中的对象
session = scoped_session(Session) # 得到一个线程安全的session,每一个线程用自己的session.以后要知道为什么用这个session

#知识点2:增
user_obj = User(name='egg',email='111@qq.com',extra='AAA')
session.add(user_obj) # 将需要写到数据库中的对象add到session这个空间里面
session.add_all([user_obj3,teacher_obj4]) # 批量增,可以增加不同的对象

#知识点3:删
res = session.query(User).filter_by(name='xxx4').delete() #先查在删,前面的部分代表查询出的结果
res = session.query(User).filter(User.id>=2).delete() # 结构特别吻合sql语句
print(res)  # res代表影响的行数

#知识点4:改
res = session.query(User).filter_by(name='egg').update({'name':'Egg'}) # 同样是先查出来,再改
res = session.query(User).filter_by(name='Egg').update({User.name:User.name+'999'},synchronize_session=False
#上面的变形,类似于Django的F查询,在原数据的基础上修改;
#固定用法:synchronize_session=False(字符串相加),synchronize_session='evaluate'(数字相加)


#知识点5:基本查询
res = session.query(User).all()  # 返回的是一个普通列表,不是Django中的链式调用
res = session.query(User).first() # 不用在all()后面.first()
#session.query(User)包含了两层含义一个是要查的字段是所有,一个是从哪张表查,即:select * from user
res = session.query(User).filter(User.id>=2).all() #filter传的是表达式
res = session.query(User).filter_by(id=1).all() #filter_by传的是参数
res = session.query(User).filter(User.id>5,User.name=='xxx5').all() #and条件
res = session.query(User).filter(and_(User.id>5,User.name=='xxx4')).all() #and条件
res = session.query(User).filter(or_(User.id>5,User.name=='xxx4')).all() #or条件


#知识点6:高级查询
res = session.query(User).filter(User.id.between(2,6)).all()
res = session.query(User).filter(User.id.in_([2,6])).all() #in是关键字,所以用in_
res = session.query(User).filter(~User.id.in_([2,6])).all() #前面加了个~,代表取反(牛逼)
res = session.query(User).filter(User.id.in_(session.query(User.id).filter_by(name='xxx4'))).all()
#二次筛选,in_()里面的列表放一个查询语句;
#查询语句只查User.id;
#可以把上面的.all()去掉,然后把res打印出来,可以看到sql语句,就是子查询
res = session.query(User.id,User.email).filter(User.id.in_([2,6])).all() #select *,与select id,email的区别;
res = session.query(User).filter(User.name.like('%3%')).all() #通配符
res = session.query(User)[1:2#limit,用于分页
res = session.query(User).order_by(User.id.desc()).all() #desc()降序,asc()升序;多个排序可以同时写在order_by()里面,逗号分隔


#知识点7:分组
#把同一类的分一个组,看看共有几组,分完组后干一些事,比如求最大值/最小值/平均值/总和/总数等等
#sql语句中,分组之后的select字段只能是:分组字段和聚合函数
res = session.query(
    func.max(User.id),
    func.sum(User.id),
    func.min(User.id),
    User.name).group_by(User.name).all()
#分组之后的having筛选:是对聚合函数的结果做筛选,比如总和大于100等等
res = session.query(
    func.max(User.id),
    func.sum(User.id),
    func.min(User.id),
    User.name).group_by(User.name).having(func.max(User.id)>5).all()
    

#知识点9:执行原生sql
session.execute('insert into users(name,email,extra) values(:name,:email,:extra)',
                params={'name':'ckg','email':'ckg@qq.com','extra':'CKG'})
                
                
session.commit()
session.close()  # 并不是真正的关闭连接,而是放回池子当中

一对多增加/查询

一对多,多对多等关系首先是人为定义的,来源于现实世界的客观约束或主观需要,比如一夫一妻;

搞懂一对多/多对多使用的关键点是搞明白relationship(backref=...)

#一对多关系:一个公司可以有多名员工,而一个员工只能属于一家公司
#一对多的关系,外键字段写在多的一方
class Company(Base):
    __tablename__ = 'company'
    id = Column(Integer,primary_key=True)
    company_name = Column(String(32))

class Person(Base):
    __tablename__ = 'person'
    pid = Column(Integer,primary_key=True)
    name = Column(String(32))
    company_id = Column(Integer,ForeignKey('company.id')) # 外键; 指向:表名.字段; 默认可以为空
    company = relationship('Company',backref='pers'
    #知识点1:
    # relationship代表不会在数据库中新建实实在在的字段;第一个参数代表类名;
    # backref参数用于反向查询:从Company查到其下所有的员工(Company.pers)


-------------------------------------------------
#知识点2:一对多关系的表新增(两张表及外键字段都能正常插入)
#方式1:
p = Person(name='egg',company=Company(company_name='EastWest'))
session.add(p)
#方式2:(方式1的拆解:先建Person对象,给对象的公司属性赋值)
p = Person(name='jason')
p.company=Company(company_name='TH')
session.add(p)
#方式3:(与方式2相反:先建Company对象,通过backref参数指定的pers来为公司的员工属性赋值;由于是一对多关系,所以值是列表)
company = Company(company_name='yktx')
company.pers = [Person(name='cqf'),Person(name='ljq')]
session.add(company)


---------------------------------------------------
#知识点3:一对多关系的查询
#方式1:基于对象的跨表查询(底层查两次)
p = session.query(Person).filter_by(name='cqf').first() #先查出员工
print(p.company.company_name) #再查员工的公司(正查)
c = session.query(Company).filter_by(company_name='yktx').first() #先查公司
print(c.pers) #再查公司所有的员工(反查)

#方式2:基于联表的跨表查询(底层查一次)
# 默认根据外键联表,可以自己指定(但没意义)
person_list = session.query(Person,Company).join(Company,isouter=True).all() #Person左连接Company
print(person_list)
#isouter默认是False,代表inner join; True代表left join; 没有right join,通过调换类的书写顺序来实现

person_list = session.query(Person,Company).filter(Person.company_id==Company.id).all()
print(person_list) # 与上面一样的效果,但是sql语句是不一样的.

多对多增加/查询

#多对多的关系
class Teacher(Base):
    __tablename__ = 'teacher'
    id = Column(Integer,primary_key=True)
    teacher_name = Column(String(32),unique=True)

class Stu(Base):
    __tablename__ = 'student'
    id = Column(Integer,primary_key=True)
    stu_name = Column(String(32),unique=True)
    teachers = relationship('Teacher',secondary='teacher2student',backref='students')
    #知识点1:
    #多对多关系中,一个学生对多个老师,所以用复数teachers;relationship代表非实体字段,写在哪个"多"的一边均可
    #secondary='teacher2student',多对多关系有一个中间表连着两头,这里指定中间表
    #backref='students',代表反向查询,当从teacher对象查学生对象时可以用

class T2S(Base):  # 第三张关系表,是实实在在存在的表,必须手动建
    __tablename__ = 'teacher2student'
    id = Column(Integer,primary_key=True,autoincrement=True)
    tea_id = Column(Integer,ForeignKey('teacher.id'))
    stu_id = Column(Integer,ForeignKey('student.id'))
  
-----------------------------------------------
#知识点2:多对多表新增数据
#方式1:正向插入
s = Stu(stu_name='ly')
s.teachers = [Teacher(teacher_name='egon'),Teacher(teacher_name='lqz')]
session.add(s)
#方式2:反向插入
t = Teacher(teacher_name='jason')
t.students = [Stu(stu_name='xxq'),Stu(stu_name='hjj')] #relationship(...,backref='students')这个参数指定的
session.add(t)


---------------------------------------------
#知识点3:多对多关系的查询
#方式1:基于对象的跨表查询(先查一个,在.出来)
t = session.query(Teacher).filter_by(teacher_name='jason').first()
print(t.students)
#方式2:基于联表的跨表查询
res = session.query(Stu).join(T2S).join(Teacher).filter(Teacher.teacher_name=='jason').all()
print(res)

下一篇讲解flask-sqlachemy

分类:

后端

标签:

Python

作者介绍

公众号:uncle39py
V1