Flask sqlalchemy 过滤器 objects 在每个 object 的关系中
Flask sqlalchemy filter objects in relationship for each object
如何在一个命令中过滤关系中的objects?
示例过滤器:我有 childrens 的列表,每个 children 都有 玩具。告诉我如何过滤每个 child 的玩具,以便列表 child.toys 只包含红色玩具。
class Child(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
toys = db.relationship('Toy', lazy='dynamic')
class Toy(db.Model):
id = db.Column(db.Integer, primary_key=True)
color = db.Column(db.String(40))
child_id = db.Column(db.Integer, db.ForeignKey('child.id') )
Child id
Child name
1
First
2
Second
Toy id
Toy color
Toy child_id
1
Blue
1
2
Red
1
3
Orange
2
4
Red
2
python 列表中的所需输出:
Child id
Child name
Toy id
Toy color
1
First
2
Red
2
Second
4
Red
编辑:
此 table 将由以下人员打印:
for child in filtered_children:
for toy in child.toys:
print(f'{child.id} {child.name} {toy.id} {toy.color}')
动态关系加载器提供了正确的结果,但您必须在 for 循环中像这样迭代:
children = Child.query.all()
for child in children:
child.toys = child.toys.filter_by(color='Red')
len( children[0].toys ) #should by one
len( children[1].toys ) #should by one
有没有办法在没有 for 循环的情况下从关系中过滤 objects?
编辑:
重新表述的问题:有没有一种方法可以在外部查询中应用过滤器,这样就不需要在 for 循环内进行额外的过滤,这样每个 child 中的每个 child.toys 属性循环将只包含红色玩具?
看来您已经配置了一些允许使用 join conditions, the next step is simply to actually use the Query.join()
调用的基本关系。
我下面的示例直接使用 SQLAlchemy,因此您可能需要调整对 Flask-SQLALchemy 特定引用的引用(例如,您可以使用 db.session
,而不是创建会话,我从 Quickstart;还有Child
和Toy
类我用inherit from a commonsqlalchemy.ext.declarative.declarative_base()
也是同理,否则下面介绍的原理应该是通用的) .
首先,导入并设置 类 - 为了保持这种通用性,我将按照说明直接使用 sqlalchemy
:
from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, contains_eager
Base = declarative_base()
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
name = Column(String(40))
toys = relationship('Toy')
class Toy(Base):
__tablename__ = 'toy'
id = Column(Integer, primary_key=True)
color = Column(String(40))
child_id = Column(Integer, ForeignKey('child.id') )
请注意,我们删除了 Child.toys
关系构造的额外关键字参数,因为指定的 'dynamic'
加载样式与所需的查询不兼容。
然后设置引擎并添加根据您的问题提供的数据:
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
session.add(Child(name='First'))
session.add(Child(name='Second'))
session.add(Toy(color='Blue', child_id=1))
session.add(Toy(color='Red', child_id=1))
session.add(Toy(color='Orange', child_id=2))
session.add(Toy(color='Red', child_id=2))
session.commit()
添加数据后,我们可以看到在从 SQLAlchemy 加载关系的文档中,有一个 rather comprehensive set of relationship loading API, and the contains_eager
适合您的用例,因为它指出“给定的属性应该是急切地从查询中手动声明的列中加载。”那么让我们看看它的实际效果:
filtered_children = session.query(Child).join(Child.toys).filter(
Toy.color=='Red'
).options(
contains_eager(Child.toys)
).all()
此查询确保通过 Child
声明的 toys
关系与查询连接,并且我们还按 "Red"
玩具进行过滤,并可选择表明 Child.toys
应该“急切地从查询中手动声明的列中加载”,这样它将通过每个返回的 child
对象变得可用。
现在,看看你最近想要的循环 filtered_children
和他们的 toys
产生你想要的输出:
for child in filtered_children:
for toy in child.toys:
print(f'{child.id} {child.name} {toy.id} {toy.color}')
应生成以下输出:
1 First 2 Red
2 Second 4 Red
如果我们启用了日志记录,我们将看到以下输出表明 SQLAlchemy 引擎发出的 SELECT
语句:
INFO:sqlalchemy.engine.base.Engine:SELECT toy.id AS toy_id, toy.color AS toy_color, toy.child_id AS toy_child_id, child.id AS child_id, child.name AS child_name
FROM child JOIN toy ON child.id = toy.child_id
WHERE toy.color = ?
INFO:sqlalchemy.engine.base.Engine:('Red',)
DEBUG:sqlalchemy.engine.base.Engine:Col ('toy_id', 'toy_color', 'toy_child_id', 'child_id', 'child_name')
DEBUG:sqlalchemy.engine.base.Engine:Row (2, 'Red', 1, 1, 'First')
DEBUG:sqlalchemy.engine.base.Engine:Row (4, 'Red', 2, 2, 'Second')
请注意,只执行了一个 SELECT ... JOIN
查询,没有对每个 Child
进行额外的查询,这将对性能产生重大影响。
如何在一个命令中过滤关系中的objects?
示例过滤器:我有 childrens 的列表,每个 children 都有 玩具。告诉我如何过滤每个 child 的玩具,以便列表 child.toys 只包含红色玩具。
class Child(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
toys = db.relationship('Toy', lazy='dynamic')
class Toy(db.Model):
id = db.Column(db.Integer, primary_key=True)
color = db.Column(db.String(40))
child_id = db.Column(db.Integer, db.ForeignKey('child.id') )
Child id | Child name |
---|---|
1 | First |
2 | Second |
Toy id | Toy color | Toy child_id |
---|---|---|
1 | Blue | 1 |
2 | Red | 1 |
3 | Orange | 2 |
4 | Red | 2 |
python 列表中的所需输出:
Child id | Child name | Toy id | Toy color |
---|---|---|---|
1 | First | 2 | Red |
2 | Second | 4 | Red |
编辑: 此 table 将由以下人员打印:
for child in filtered_children:
for toy in child.toys:
print(f'{child.id} {child.name} {toy.id} {toy.color}')
动态关系加载器提供了正确的结果,但您必须在 for 循环中像这样迭代:
children = Child.query.all()
for child in children:
child.toys = child.toys.filter_by(color='Red')
len( children[0].toys ) #should by one
len( children[1].toys ) #should by one
有没有办法在没有 for 循环的情况下从关系中过滤 objects?
编辑: 重新表述的问题:有没有一种方法可以在外部查询中应用过滤器,这样就不需要在 for 循环内进行额外的过滤,这样每个 child 中的每个 child.toys 属性循环将只包含红色玩具?
看来您已经配置了一些允许使用 join conditions, the next step is simply to actually use the Query.join()
调用的基本关系。
我下面的示例直接使用 SQLAlchemy,因此您可能需要调整对 Flask-SQLALchemy 特定引用的引用(例如,您可以使用 db.session
,而不是创建会话,我从 Quickstart;还有Child
和Toy
类我用inherit from a commonsqlalchemy.ext.declarative.declarative_base()
也是同理,否则下面介绍的原理应该是通用的) .
首先,导入并设置 类 - 为了保持这种通用性,我将按照说明直接使用 sqlalchemy
:
from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, contains_eager
Base = declarative_base()
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
name = Column(String(40))
toys = relationship('Toy')
class Toy(Base):
__tablename__ = 'toy'
id = Column(Integer, primary_key=True)
color = Column(String(40))
child_id = Column(Integer, ForeignKey('child.id') )
请注意,我们删除了 Child.toys
关系构造的额外关键字参数,因为指定的 'dynamic'
加载样式与所需的查询不兼容。
然后设置引擎并添加根据您的问题提供的数据:
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
session.add(Child(name='First'))
session.add(Child(name='Second'))
session.add(Toy(color='Blue', child_id=1))
session.add(Toy(color='Red', child_id=1))
session.add(Toy(color='Orange', child_id=2))
session.add(Toy(color='Red', child_id=2))
session.commit()
添加数据后,我们可以看到在从 SQLAlchemy 加载关系的文档中,有一个 rather comprehensive set of relationship loading API, and the contains_eager
适合您的用例,因为它指出“给定的属性应该是急切地从查询中手动声明的列中加载。”那么让我们看看它的实际效果:
filtered_children = session.query(Child).join(Child.toys).filter(
Toy.color=='Red'
).options(
contains_eager(Child.toys)
).all()
此查询确保通过 Child
声明的 toys
关系与查询连接,并且我们还按 "Red"
玩具进行过滤,并可选择表明 Child.toys
应该“急切地从查询中手动声明的列中加载”,这样它将通过每个返回的 child
对象变得可用。
现在,看看你最近想要的循环 filtered_children
和他们的 toys
产生你想要的输出:
for child in filtered_children:
for toy in child.toys:
print(f'{child.id} {child.name} {toy.id} {toy.color}')
应生成以下输出:
1 First 2 Red
2 Second 4 Red
如果我们启用了日志记录,我们将看到以下输出表明 SQLAlchemy 引擎发出的 SELECT
语句:
INFO:sqlalchemy.engine.base.Engine:SELECT toy.id AS toy_id, toy.color AS toy_color, toy.child_id AS toy_child_id, child.id AS child_id, child.name AS child_name
FROM child JOIN toy ON child.id = toy.child_id
WHERE toy.color = ?
INFO:sqlalchemy.engine.base.Engine:('Red',)
DEBUG:sqlalchemy.engine.base.Engine:Col ('toy_id', 'toy_color', 'toy_child_id', 'child_id', 'child_name')
DEBUG:sqlalchemy.engine.base.Engine:Row (2, 'Red', 1, 1, 'First')
DEBUG:sqlalchemy.engine.base.Engine:Row (4, 'Red', 2, 2, 'Second')
请注意,只执行了一个 SELECT ... JOIN
查询,没有对每个 Child
进行额外的查询,这将对性能产生重大影响。