111、Sqlalchemy、Scoped_session、基本的增删改查、一对多(一对一)、多对多

354 阅读8分钟

今日内容概要

  • Sqlalchemy
  • Scoped_session
  • 基本的增删改查
  • 一对多(一对一)
  • 多对多

今日内容详细

Sqlalchemy

操作数据

models.py

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

Base = declarative_base()


# print(type(Base))
class User(Base):
    # 以__开头的是配置
    __tablename__ = 'users'  # 数据库表名称,如果不写,以类名作为表名

    id = Column(Integer, primary_key=True)  # 主键索引,聚簇索引
    name = Column(String(64), index=True, nullable=False)  # name字段加辅助索引
    email = Column(String(32), unique=True)
    # datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
    ctime = Column(DateTime, default=datetime.datetime.now)
    extra = Column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint('id', 'name', name='uix_id_name'),  # 联合唯一
        Index('ix_id_name', 'name', 'email'),  # 索引
    )

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


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

    def __str__(self):
        return self.caption

    def __repr__(self):
        return self.caption


class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)  # 不会自动生成id
    name = Column(String(32), index=True, nullable=True)
    # hobby指的是tablename而不是类名
    # 一对多关系一旦确立,关联关系写在多的一方---》物理外键
    hobby_id = Column(Integer, ForeignKey("hobby.id"))

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

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


# 多对多关系
# 中间表  手动创建
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'))


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

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


class Boy(Base):
    __tablename__ = 'boy'
    id = Column(Integer, primary_key=True, autoincrement=True) #autoincrement 默认就是true
    name = Column(String(64), unique=True, nullable=False)

    # 就是咱们之前的ManyToMany,不会在表中生成字段---》因为它是个表----》这个字段可以放在Girl表
    girls = relationship('Girl', secondary='boy2girl', backref='boys')

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

执行文件

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base,User
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 创建表,删除表
#1 在数据库中创建表
# Base.metadata.create_all(engine)
# 2 删除表
# Base.metadata.drop_all(engine)
1.创建表:Base.metadata.create_all(engine)
2.删除表:Base.metadata.drop_all(engine)

操作表中得数据

import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base,User
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

# 操作数据表中的数据
# 1.第一步:创建engine对象

# 2.第二步:得到一个session对象--->不是flask的session--->会话--->链接
Session = sessionmaker(bind=engine) # 把引擎传入
session = Session()  # 得到session对象

# 3.第三步:使用session对象操作数据
# 3.1 创建对象
user= User(name = 'nana',email='11@qq.com')
# 3.2 保存到数据库
session.add(user)
# 3.3 提交事务、关闭会话
session.commit()  # 提交事务
session.close() #关闭会话

scoped_session-->保证线程安全

1.如果集成到flask中,session会话是要做成全局,还是每个视图函数有自己的一个?
	应该做成,每个视图函数,都新创建一个session对象
	这样每次都要加括号得到session对象
  """
    Session = sessionmaker(bind=engine) # 把引擎传入
		session = Session()  # 得到session对象
  """

2.scoped_session 全局只有一个session对象,在不同视图函数就用这一个--->保证线程安全
	-做成了每个线程自己一个单独的session对象
    
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
from sqlalchemy.engine.base import Engine
from models import Base,User
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

#操作数据表中的数据
# 1.第一步:创建engine对象
# 2.第二步:得到一个session对象--->不是flask的session--->会话--->链接
Session = sessionmaker(bind=engine) # 把引擎传入
# session = Session()  # 得到session对象
# print(type(session))  # <class 'sqlalchemy.orm.session.Session'>
session = scoped_session(Session)  # # 这个session全局用一个即可
print(type(session))  # <class 'sqlalchemy.orm.scoping.scoped_session'>


# 3.第三步:使用session对象操作数据
# 3.1 创建对象
user= User(name = 'xiao',email='222@qq.com')
# 3.2 保存到数据库
session.add(user)
#3.3 提交事务、关闭会话
session.commit()  # 提交事务
session.close() #关闭会话

研究:Session 的区别和联系scoped_session

from sqlalchemy.orm.session import Session
from sqlalchemy.orm.scoping import scoped_session
1.思考:scoped_session 没有add方法,调用的时候,会有?
	使用到类装饰器了

scoped_session 总结

1.以后scoped_session的对象,就像使用Session的对象一样用--->装饰器放进去了
2.scoped_session 是线程安全的,如何做到的
  -每个线程自己的一个session对象
  -self.registry = ThreadLocalRegistry(session_factory)
        
3.t=threading.local()  很神奇,多线程并发操作,不需要加锁,不会出现并发安全问题,每个线程用的都是自己的那个数据
  -核心原理是:通过线程id号做个区分
  -线程1 t.a=88  内部 ---》{线程id号:{a:88}}
  -线程1 t.a=77  内部 ---》{线程id号:{a:88},线程id号:{a:77}}
    线程1   t.a=100  --->在当前线程中  print(t.a)   ---》100
    线程2   t.a=99  --->在当前线程中  print(t.a)    ---》99
  -不同线程用的都是t对象threading.local(),但是每个线程用自己的数据

类装饰器

1.加在类上的装饰器
def auth(func):
    def inner(*args,**kwargs):
        res=func(*args,**kwargs)
        res.add='999'
        return res
    return inner

@auth  # Person=auth(Person)
class Person():
    pass


# 执行:Person()----->在执行---》inner
p=Person()  # inner()  inner的返回值给了p
print(p.add)


2.类作为装饰器

基本增删查改

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

engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Session = sessionmaker(bind=engine)  # 把引擎传入
session = Session()

增:add、add_all

session.add(User(name = 'aa',email='@qq.com'))
user1 = User(name = 'aa1',email='1@qq.com')
user2 = User(name = 'aa2',email='2@qq.com')
user3 = User(name = 'aa3',email='3@qq.com')
session.add_all([user1,user2,user3])

查:filter、filter_by

1.filter传的是表达式,filter_by传的是参数
2.all()  出来的是列表--->不是qs对象,没有这个东西
3.想拿单条 .first()
4.
# select * from User where User.id>2 limit 1;
res = session.query(User).filter(User.id == 1).first()
print(res)  # nana
res1 = session.query(User).filter(User.id >2).first()
print(res1)  # xiao
res2 = session.query(User).filter(User.id >2).all()
print(res2)  # [xiao, aa, aa1, aa2, aa3]
res=session.query(User).filter_by(id=4).all()
print(res)  # [xiao]

删:delete()

# delete * from User where id >6;
res = session.query(User).filter(User.id>=6).delete()
print(res)  # 3 影响的行数


ps: user.delete()不能删除,没有方法
	user=session.query(User).filter(User.id == 5).first()
	user.delete()  # 它没有单独删对象的

改:update()

res = session.query(User).filter(User.id >0).update({"name":"nana"})
print(res)  # 4

#类似于django的F查询
  res1 = session.query(User).filter(User.id >0).update({User.name:User.name+'ok'},synchronize_session=False) # # 字符串相加
  print(res1)  # 4
  session.query(User).filter(User.id > 0).update({"age": User.age + 1}, synchronize_session="evaluate") # 数字相加

# 思考:查到单个对象,修改属性-->add增加进去--->修改? --->add 只要有id,就是修改
  res=session.query(User).filter(User.id == 1).first()
  print(res)
  res.name='yyyy'
  session.add(res)

一对多(一对一)

表模型

一对一
一对多
多对多
----都是外键关系-----  一对一其实是 一对多 的一种特例

models.py

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

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

    def __str__(self):
        return self.caption

    def __repr__(self):
        return self.caption

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)  # 不会自动生成id
    name = Column(String(32), index=True, nullable=True)
    # hobby指的是tablename而不是类名
    # 一对多关系一旦确立,关联关系写在多的一方---》物理外键
    hobby_id = Column(Integer, ForeignKey("hobby.id"))

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

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
from sqlalchemy.engine.base import Engine
from models import Base,Person,Hobby
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Session = sessionmaker(bind=engine) # 把引擎传入
session = Session()  # 得到session对象

新增和基于对象的查询

1.增加数据:方式一
session.add(Hobby())
session.add(Person(name = 'nana',hobby_id = 1))
session.add(Person(name='彭于晏',hobby_id=2))  # 报错

2.增加数据:方式二
session.add(Person(name = 'cx',hobby=Hobby(caption='羽毛球')))

查询

hobby = session.query(Hobby).filter_by(id =2).first()
print(hobby)  # 羽毛球
# 需求:查所有喜欢羽毛球的人   爱好-->人 反  relationship('Hobby', backref='pers')   按 backre
print(hobby.pers)  # [cx]

# 需求:id为1的人的爱好   正
p = session.query(Person).filter_by(id = 1).first()
print(p)  # nana
print(p.hobby)  # 篮球
print(p.hobby.caption)  # 篮球
print(p.hobby_id) # 1

修改 和删除 跟之前一样

多对多

表模型

# 多对多关系
# 中间表  手动创建
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'))

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

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name


class Boy(Base):
    __tablename__ = 'boy'
    id = Column(Integer, primary_key=True, autoincrement=True) #autoincrement 默认就是true
    name = Column(String(64), unique=True, nullable=False)

    # 就是咱们之前的ManyToMany,不会在表中生成字段---》因为它是个表----》这个字段可以放在Girl表
    girls = relationship('Girl', secondary='boy2girl', backref='boys')

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
from sqlalchemy.engine.base import Engine
from models import Base,Person,Hobby
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
    "mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
    max_overflow=0,  # 超过连接池大小外最多创建的连接
    pool_size=5,  # 连接池大小
    pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
    pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
)

Session = sessionmaker(bind=engine) # 把引擎传入
session = Session()  # 得到session对象

新增

1.方式一:笨办法
girl = Girl(name ='nana')
boy = Boy(name = 'cx')
session.add_all([girl,boy])
# 操作中间表(纯手动操作中间表)
session.add(Boy2Girl(girl_id = 1,boy_id = 1))

2.方式二:使用relationship
boy = Boy(name = 'xiao')
boy.girls = [Girl(name = 'ln'),Girl(name='luo')]  # 增加了俩girl
#给这一个boy,增加了两条约会记录
session.add(boy)

查询

# 基于对象跨表查询
# girls = relationship('Girl', secondary='boy2girl', backref='boys')

# 正向
boy = session.query(Boy).filter_by(id = 2).first()
print(boy.girls)  # [ln, luo]

# 反向
girl = session.query(Girl).filter_by(id = 1).first()
print(girl.boys)  # [cx]