Python SQLAlchemy:使用子查询

时间:2017-09-13 13:53:20

标签: python python-3.x sqlalchemy

在sqlalchemy中,我为我的数据库定义了一个模型,在这种情况下,两个表“sample”和“experiment”,它们通过多对多关系相互链接:

class Sample(Base):
    __tablename__ = 'sample'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    date = Column(String)

class Experiment(Base):
    __tablename__ = 'experiment'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    description = Column(String)
    result = Column(String)

    samples = relationship('Sample', secondary='sample_has_experiment', backref="experiments")

t_sample_has_experiment = Table(
    'sample_has_experiment', metadata,
    Column('sample_id', ForeignKey('sample.id'), primary_key=True, nullable=False, index=True),
    Column('experiment_id', ForeignKey('experiment.id'), primary_key=True, nullable=False, index=True)
)

在我的数据库中,我有一个样本“达丽星”,在实验中有两个实验“冷融合”和“蠕虫洞”。

我尝试通过连接到样本表来查询“蠕虫洞”实验:

samples = s.query(Obj.Sample).join(Obj.Sample.experiments).\
    filter(Obj.Experiment.name == "Worm hole").all()


for sample in samples:
    for experiment in sample.experiments:
        print(experiment.name)

但结果我仍然得到了两个实验“蠕虫洞”和“冷融合”。所以看起来似乎没有应用过滤。如何过滤我只收到“蠕虫洞”实验对象的方式?谢谢。

@Dublicate:确实它看起来像是同一个问题,但是给出的答案并没有解决我的问题。在我看来,我建议的查询与提议的答案完全一样。

2 个答案:

答案 0 :(得分:1)

您的代码所说的是:

  • 对于所有属于虫洞实验的样本

    • 打印样本属于
    • 的所有实验

也就是说,给定一个特定样本,sample.experiments始终是样本所属的所有实验,而不仅仅是您通过该样本进行的实验。

如果你要添加一个不属于虫洞实验的新样本,你可以看到这个。它不应出现在您的查询中。

所以,我的回答是"为什么我的查询不会对连接进行过滤,"是我强烈怀疑它是。

如果您想要虫洞实验中的样本对象,那么

samples = session.query(Experiments).filter_by(name = 'wormhole').one().samples

答案 1 :(得分:0)

编辑:其实我第一次意识到我的错误是什么。我必须查询所有表,然后应用连接和过滤器:

qry = s.query(Obj.Sample, Obj.Experiment).\ # Query both tables!
    filter(Obj.Sample.name == "...").\
    filter(Obj.Experiment.name == "...").\
    join(Obj.Experiment, Obj.Sample.experiments).all()

替代方法:使用子查询会产生所需的行为:

wormhole_exp = s.query(Obj.Experiment).filter(Obj.Experiment.name == "wormhole").subquery()

print(s.query(Obj.Sample, wormhole_exp).join(wormhole_exp, Obj.Sample.experiments).one())