在SQLAlchemy的ORM

时间:2016-11-30 18:48:06

标签: python sqlalchemy

我试图欺骗sqlalchemy做一些奇怪的查询。我需要编写一个查询,在一对多数据库中选择父对象,其中子对象上的数据列的值不同于同一父对象的其他子对象的值。我知道在执行查询后我可以使用python轻松完成此操作,但出于性能考虑,我希望尽可能使用sqlalchemy的API来实现。

以下最小例子解释了我尝试做的事情。其中大部分是设置,因此只有最后两个代码块才与问题真正相关。

from sqlalchemy import Column, Integer, Enum, String, ForeignKey, create_engine
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from collections import namedtuple

Base = declarative_base()

Data_t = namedtuple('Data_t', 'data1, data2, data3')("1", "2", "3")

class Parent(Base):
    __tablename__ = 'parents_table'

    id = Column(Integer, primary_key=True)
    children = relationship('Child')

    def __repr__(self):
        return ("Parent %s" % self.id)


class Child(Base):
    __tablename__ = 'children_table'

    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parents_table.id'))
    parent = relationship('Parent', back_populates='children')

    data = Column(String, Enum(*Data_t._asdict().values(), name='data'))

    def __init__(self, _parent, data):
        self.parent = _parent
        self.parent_id = _parent.id
        self.data = data

    def __repr__(self):
        return "Child %s: %s" % (self.id, self.data)

# Create session
engine = create_engine('sqlite:///:memory:', echo=False)
engine.connect()
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
Session.configure(bind=engine)
session = Session()

# Create parents
parent1, parent2 = Parent(), Parent()
session.add(parent1)
session.add(parent2)
session.commit()

# Create children with differing data column values
for i in range(3):
    child = Child(parent1, str(i + 1))
    session.add(child)
# Create children with the same data column value
for i in range(3):
    child = Child(parent2, str(1))
    session.add(child)
session.commit()

# Run query - This query pulls all the contents of the parent table
# whereas I'd like to only select the parents where the children
# data values differ - i.e. I'd like to select parent 1, but not parent 2
for parent in session.query(Parent).all():
    print(parent)
    for child in parent.children:
        print("   ", str(child))

此代码输出以下内容:

Parent 1
    Child 1: 1
    Child 2: 2
    Child 3: 3
Parent 2
    Child 4: 1
    Child 5: 1
    Child 6: 1

我希望能够只选择父母1,但我还没有在sqlalchemy的文档中找到任何能够产生此类查询的文档。< / p>

2 个答案:

答案 0 :(得分:1)

过滤器的关键是COUNT(DISTINCT ..) SQL子句:

from sqlalchemy import func
parents = (
    session
    .query(Parent)
    .outerjoin(Child)
    .group_by(Parent)
    .having(func.count(Child.data.distinct()) > 1)
).all()

for parent in parents:
    print(parent)
    for child in parent.children:
        print("   ", str(child))

奖励:如果您确实需要获取Child,并且只想在一个SQL语句中执行此操作,则可以在查询中添加以下选项,以便还会在同一查询中检索子项:

    .options(joinedload(Parent.children))  # from sqlalchemy.orm import joinedload

答案 1 :(得分:0)

您可以按ID:

过滤查询
for parent in session.query(Parent).filter(id==1):
    print(parent)
    for child in parent.children:
        print("   ", str(child))