简体   繁体   English

Python SQLAlchemy:使用子查询过滤联接的表

[英]Python SQLAlchemy: Filter joined tables with subqueries

In sqlalchemy I defined a model for my database, in this case two tables "sample" and "experiment", which are linked over a many-to-many relationship to each other: 在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)
)

In my database, I have a sample "Dilithium", and in experiments two experiments "Cold fusion" and "Worm hole". 在我的数据库中,我有一个示例“双锂”,在实验中有两个实验“冷聚变”和“蠕虫洞”。

I try to query the "Worm hole" experiment over a join to the sample table: 我尝试通过对样本表的联接查询“蠕虫洞”实验:

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)

But as a result I still get both experiments "Worm hole" AND "Cold Fusion". 但是结果还是我得到了“蠕虫洞”和“冷融合”这两个实验。 So it looks like the filtering is not applied. 因此,似乎未应用过滤。 How can I filter that way that I only receive the "Worm Hole" Experiment object? 如何过滤只收到“蠕虫洞”实验对象的方式? Thank you. 谢谢。

@Dublicate: Indeed it looks like the same question, but the given answer does not solve my problem. @Dublicate:确实看起来像是同样的问题,但是给出的答案并不能解决我的问题。 In my opinion, my suggested query does exactly the same like in the proposed answer. 我认为,建议的查询与建议的答案完全相同。

What your code says is: 您的代码说的是:

  • For all samples that were part of the wormhole experiment 对于虫洞实验中的所有样品

    • Print all experiments that sample is part of 打印样本属于的所有实验

That is, given a particular sample, sample.experiments is always all the experiments that sample belongs to not just the experiment you got to that sample through. 也就是说,对于特定的样本, sample.experiments始终是样本所属的所有实验,而不仅仅是您通过该样本获得的实验。

You can see this if you go add a new sample that does not belong to the wormhole experiment. 如果您添加不属于虫洞实验的新样品,则可以看到此信息。 It should not appear in your query. 它不应出现在您的查询中。

So, my answer to "why isn't my query filtering on the join," is that I strongly suspect it is. 因此,我对“为什么我的查询不过滤联接的答案”的回答是,我强烈怀疑是这样。

If you want the sample objects that were in the wormhole experiment then something like 如果您想要虫洞实验中的样本对象,则类似

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

Edit: Actually I realized what my mistake in the first place was. 编辑:实际上,我首先意识到我的错误是。 I have to query all tables and then apply joins and filters: 我必须查询所有表,然后应用联接和过滤器:

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()

Alternative way: The use of a subquery leads to the desired behaviour: 替代方法:使用子查询可产生所需的行为:

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())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM