sqlalchemy in查询优化_Python之SQLAlchemy+ORM

本文介绍了如何使用SQLAlchemy进行外键和约束条件的设置,包括等值、非等值、模糊、多值、空值查询。详细讲解了外键的创建,以及RESTRICT、CASCADE、SET NULL约束。接着探讨了ORM层面的一对多、一对一和多对多关系,提供代码示例,特别强调了一对一关系通过`uselist=False`实现,并展示了多对多关系的联合主键设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天的SQLAlchemy操作数据库的有一些操作没有学完,今天把它们补上,分别是filter条件查询的过滤条件、以及SQLAlchemy实现外键和四种约束条件,然后继续学习了在ORM层面外键一对多关系一对一关系多对多关系的相关操作。

78216ad12138211f7cc9f4b0f7d3839f.png

filter条件查询过滤条件

ps:
以下的写法只是针对条件查询filter而言
如果想看底层转化的sql语句,打印的时候去掉frist()或者all()
  • 等值查询equals(==),非等值查询not equals(!=
  • 模糊查询:like(用的较多)& ilike(不区分大小写)
  • 多值查询 in_ ;取反操作 not in
  • 空查询 is null & 非空查询 is not null
  • 多条件查询 and & or
    • 并且查询 and_
    • 或者查询 or_

代码实现

# 需求:sqlalchemy条件查询(filter函数)和常用的过滤条件有哪些?

# 共两种查询,过滤的方法
# r1 = session.query(News).filter(News.id == 1).first()
# print(r1)
# r2 = session.query(News).filter_by(id = 2).first()
# print(r2)

# 1、equal  ==   &  not equal  !=
# news = session.query(News).filter(News.title == "标题1").first()
# news = session.query(News).filter(News.title != "标题1").all()
# print(news)

# 2、like  &  ilike  (不区分大小写)
# news = session.query(News).filter(News.title != "标题%").all()
# print(news)

# 3、 in  & not in
# news = session.query(News).filter(News.title.in_(['标题1','标题2'])).all()
# print(news)

#  not in  两种方法

# news = session.query(News).filter(News.title.notin_(['标题1','标题2'])).all()
# print(news)
# news = session.query(News).filter(~News.title.in_(['标题1','标题2'])).all()
# print(news)

# 4、 is nll   & isnot null
# news = session.query(News).filter(News.content == None).all()
# print(news)
# news = session.query(News).filter(News.content != None).all()
# print(news)


# 5、多条件查询  and   &  or
# news = session.query(News).filter(News.title =='标题1',News.content.like('%ads%')).all()
# print(news)
news = session.query(News).filter(and_(News.content.like('%a%'),News.title == '标题1')).all()
print(news)

SQLAlchemy实现外键及其四种约束

实现外键以及四种约束,不得不说的就是表关系,表关系很好理解,无非就是一对一,一对多,多对多这三种关系,那么这三种关系后面会使用ORM来模拟的,现阶段先学习怎么样创建外键,以及表关系有哪四种约束关系

创建外键

简而言之就是:在从表中增加一个字段,指定这个字段外键是哪个表的哪个字段就可以了。需要使用sqlalchemy.ForeignKey()函数来创建外键

注意:从表中外键的字段,必须和主表的主键字段类型保持一致。
uid = Column(Integer,ForeignKey(User.id))
uid = Column(Integer,ForeignKey('user.id'))

四种约束(三种)

说是四种约束,其实是三种约束,因为第二种约束NO ACTION,在mysql关系型数据库中和RESTRICT一样一样的。

  • RESTRICT(默认项):若子表中有父表对应的关联数据,删除父表对应数据,会阻止删除
  • NO ACTION:在MySQL中,同RESTRICT
  • CASCADE:级联删除
    举个栗子 : 一个班级里面有很多个学生,假如把这个班级删除了,那么里面的学生也没了
  • SET NULL:父表对应数据被删除,子表对应数据项会设置为NULL,滞空删除
    只删除外键表,不删除主表里的数据

代码实现

# 需求 :SQLAlchemy实现外键以及四种约束
# 用户表  
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer,autoincrement= True,primary_key=True)
    uname = Column(String(50),nullable=False)
    def __repr__(self):
        return '<id={id},uname = {uname}>'.format(id=self.id,uname = self.uname)
​
# 新闻表
class News(Base):
    __tablename__ = 'news'
    id = Column(Integer,autoincrement=True,primary_key=True)
    title = Column(String(80),nullable=False)
    content = Column(Text,nullable=False)
    # 创建外键 ForeignKey,    并且默认删除策略为RESTRICT
    # uid = Column(Integer,ForeignKey(User.id))
    # uid = Column(Integer,ForeignKey('user.id'))
    # 四种约束      RESTRICT、NO ACTION、CASCADE、SET NULL
    # uid = Column(Integer, ForeignKey('user.id',ondelete="RESTRICT"))
    # uid = Column(Integer, ForeignKey('user.id', ondelete="NO ACTION"))
    # uid = Column(Integer, ForeignKey('user.id', ondelete="CASCADE"))
    uid = Column(Integer, ForeignKey('user.id', ondelete="SET NULL"))
    def __repr__(self):
        return "<id = %s,title = %s,content = %s,uid=%s>"%(self.id,self.title,self.content,self.uid)
​
Base.metadata.drop_all()
Base.metadata.create_all()
​
# 添加测试数据
user = User(uname= 'MGorz')
session.add(user)
session.commit()

ORM层面(relationship)

ORM层面外键和一对多关系

如上例子中,想要拿到那篇文章作者是谁,用mysql的外键可以拿到,但是很麻烦,代码量也很多。
所以SQLAlchemy提供了一个relationship。这个类可以定义属性,以后在访问相关联的表的时候就直接可以通过属性访问的方式就可以访问得到了SQLAlchemy.orm.relationship

优化:使用relationship时,可以把正向通过反向声明,结合backref参数。

好处:可以通过【新闻】导入【作者】,分支也ok。

代码实现(相对于上一个代码的增加)

from sqlalchemy.orm import relationship
class User(Base):
# 需求2
    # 添加属性优化两个表的查询操作
    # newss = relationship("News")
    # 上述代码优化,通常会把它通过反向声明的方式写在“多”的那一方
    
class News(Base):
# 添加属性,优化2表查询操作
    # 正向
    # author = relationship("User")
    # 最终,会把正向和反向关系写在一起
    author = relationship("User",backref = "newss")
# 需求:ORM层面外键和一对多关系
# 需求1 :查询谋篇新闻的  作者
# news = session.query(News).first()
# print(news)
# print(news.uid) # 拿到这篇新闻是谁写的  哪个id写的
# user = session.query(User).get(news.uid  == 1)
# print(user)
# print(user.uname)
# 结论上述代码,可以实现需求,但是操作太麻烦,可以引入基于ORM模型的 relationship进行优化(查询优化)
news = session.query(News).first()
print(news.author.uname)
​
# 需求2 查询XX作者的全部新闻
user = session.query(User).first()
print(user.newss)
​
​
# 需求:ORM层面外键和一对多关系,引入relationship以后的好处
# 好处1 :查询更简单
# 好处2:添加数据也进行了优化
# 可以通过添加【用户】来添加【新闻】
# user = User(uname = "lulu")
# news1 = News(title= "ccc",content = '88888888')
# news2 = News(title= "bbbb",content = '9999')
# print(type(user.newss))
​
# 关联
# user.newss.append(news1)
# user.newss.append(news2)
​
# session.add(user)
# session.commit()
​
​
# 可以通过添加【新闻】来添加【用户】
news3 = News(title = "EE",content = "asdasdas")
user = User(uname = "谈谈")
news3.author = user
print(type(news3.author))

ORM层面外键和一对一关系

在sqlalchemy中,如果想要将两个模型映射成一对一的关系,那么应该在父模型中,指定引用的时候,要传递一个uselist=False(默认是True)这个参数进去,告知父模型,以后引用这个模型,不再是一个列表,而是一个对象。

实现一对一关系是通过参数uselist = Flase实现的

具体格式

  • 方式一(主从类都有代码)
  • 方式二(只有从类里面有代码)(常用!!)
    需要sqlalchemy,orm.backref函数

好处

  • 添加数据
  • 查询数据

代码实现

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer,autoincrement= True,primary_key=True)
    uname = Column(String(50),nullable=False)
    # 需求2
    # 添加属性优化两个表的查询操作
    # newss = relationship("News")
    # 上述代码优化,通常会把它通过反向声明的方式写在“多”的那一方
    #  一对一关系的表示 方式1
    # extend = relationship("UserExtend",uselist = False)
    
class UserExtend(Base):
    __tablename__ = 'user_extend'
    id = Column(Integer,autoincrement=True,primary_key=True)
    school = Column(String(50),nullable=False)
    # 外键
    uid = Column(Integer,ForeignKey(User.id))
    # 一对一关系的表示 方式1
    # user = relationship('User')
    # 方式二  用的较多 ,常用
    user = relationship('User',backref = backref('extend',uselist = False))
​
# 需求:基于ORM外键,怎么表示一对一
# 添加数据 (好处1) User 添加UserExtend
user = User(uname = '张三')
school = UserExtend(school = "山东大学")
user.extend = school
# print(type(user.extend))
session.add(user)
session.commit()
​
​
# 添加数据 (好处1)   反方向操作 通过UserExtend添加user
school = UserExtend(school = '清华')
user2 = User(uname = "李四")
school.user = user2
# print(type(school.user))
session.add(school)
session.commit()
​
​
# 查询数据
user3 = session.query(User).first()
print(user3)
print(user3.extend.school)

ORM层面外键和多对多关系

把多对多分解为两个一对多的关系,这样就需要引入中间表实现。

联合主键:非空且唯一

举个栗子:
有两个字段,字段1和字段2 ,1对应1 ,1对应2 ,2对应1 上述都是唯一的,如果第四条数据出现了字段1 和字段 1的组合,就不是联合主键,不满足主键约束

步骤:

思想:要实现多对多的关系一定需要通过一张中间表来绑定他们之间的关系。
  1. 首先把两个需要做多对多的模型定义出来。
    class New(Base):
    __tablename__ = 'news'
    id = Column(Integer,autoincrement=True,primary_key=True)
    title = Column(String(50),nullable=False)
    def __repr__(self):
    return "<title = %s>"%(self.title)

    # 表2
    class Tag(Base):
    __tablename__ = "tag"
    id = Column(Integer,autoincrement=True,primary_key=True)
    name = Column(String(50),nullable=False)

    def __repr__(self):
    return "<name = %s>"%(self.name)
  2. 使用Table定义(sqlalchemy.Table)一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”。
    news_tag = Table(
    'news_tag',
    Base.metadata,
    # 中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”
    Column("news_id",Integer,ForeignKey('news.id'),primary_key=True),
    Column("tag_id",Integer,ForeignKey('tag.id'),primary_key=True),
    )
  3. 在两个需要做多对多的模型中随便选择一个模型,定义一个relationship属性,来绑定三者之间的关系,在使用relationship的时候,需要传入一个secondary=中间表对象名
    # c产生关系写法2
    # 本模型中定义的属性 = relationship('模型名',backref = 'tag'(ps:引号中的相当于在另一个模型中定义了这个属性),secondary = 中间表名字)
    newss = relationship('New',backref = 'tag',secondary = news_tag)

整体代码实现

# 需求:orm层面 外键和多对多关系的实现
​
# 中间表
news_tag = Table(
    'news_tag',
    Base.metadata,
    # 中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”
    Column("news_id",Integer,ForeignKey('news.id'),primary_key=True),
    Column("tag_id",Integer,ForeignKey('tag.id'),primary_key=True),
)
​
# 表1
class New(Base):
    __tablename__ = 'news'
    id = Column(Integer,autoincrement=True,primary_key=True)
    title = Column(String(50),nullable=False)
    # 产生关系的写法1
    # tag = relationship('Tag',backref = 'news',secondary = news_tag)
​
    def __repr__(self):
        return "<title = %s>"%(self.title)
      
# 表2
class Tag(Base):
    __tablename__ = "tag"
    id = Column(Integer,autoincrement=True,primary_key=True)
    name = Column(String(50),nullable=False)
    # c产生关系写法2
    # 本模型中定义的属性 = relationship('模型名',backref = 'tag'(ps:引号中的相当于在另一个模型中定义了这个属性),secondary = 中间表名字)
    newss = relationship('New',backref = 'tag',secondary = news_tag)
​
    def __repr__(self):
        return "<name = %s>"%(self.name)
​
# Base.metadata.drop_all()
# Base.metadata.create_all()
​
# 添加数据的好处
# newss1 = New(title = '世界第一')
# newss2 = New(title = '世界第二')
​
# tag1 = Tag(name = '张仨')
# tag2 = Tag(name ='李希')
​
# newss1.tag.append(tag1)
# newss1.tag.append(tag2)
​
# newss2.tag.append(tag1)
# newss2.tag.append(tag2)
​
# session.add_all([newss1,newss2])
# session.commit()
​
# 查询数据的好处
newss3 = session.query(New).first()
print(newss3.tag)
​
tag3 = session.query(Tag).first()
print(tag3.newss)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值