SQLAlchemy过滤查询中的子级,但不包括父级。

3

我有一个查询返回了一个 User 对象。 User 有许多他们发布的 Post。当我执行查询时,我希望基于特定时间过滤在 User.post 中找到的帖子。例如,只返回给定时间戳1天内发布的帖子。

class User(base):
    __tablename__ = 'User'

class Post(base):
    __tablename__ = 'Post'

    user_id = Column(Integer, ForeignKey('User.uid'))
    user = relationship(User, backref=backref('post')
    time = Column(DateTime, default=func.current_timestamp())

有没有一种方法可以动态更改ORM加载子项的方式,并在查询时指定呢?类似以下的伪代码:

User.query.filter_child(Post.time in timespan)

重要的是,如果没有符合条件的子元素,我不想过滤父元素。我只想为给定的用户过滤ORM加载的子元素。


如果不是重复的话,与 https://dev59.com/dXzaa4cB1Zd3GeqPUMJF 相关。唯一的区别在于使用 outerjoin 而不是 join - Ilja Everilä
相关,但您的链接涉及根据子属性过滤父级。在我的问题中,我不希望根据子属性过滤父级,只想通过给定属性限制加载到父级中的子项。 - melchoir55
我实际上尝试了使用filter进行outerjoin,但结果是当没有帖子与过滤器匹配时,我没有得到任何用户对象。 - melchoir55
2个回答

3

加载自定义过滤集合使用contains_eager()完成。由于目标是加载所有User对象,即使它们没有与筛选条件匹配的Post对象,应该使用outerjoin()并将帖子的谓词放在连接的ON子句中:

end = datetime(2019, 4, 10, 12, 0)
start = end - timedelta(days=1)

# If you want to use half closed intervals, replace `between` with
# suitable relational comparisons.
User.query.\
    outerjoin(Post, and_(Post.user_id == User.uid,
                         Post.time.between(start, end))).\
    options(contains_eager(User.post))

这种方法对我来说不起作用,即使我使用了outerjoin()。如果父对象没有任何与过滤条件匹配的子对象,它们将被排除在结果集之外。根据文档,这似乎是预期的行为。 然而,我需要一种方法来仍然包括父对象,但其子对象为空列表。 https://docs.sqlalchemy.org/en/20/orm/queryguide/relationships.html#using-contains-eager-to-load-a-custom-filtered-collection-result: "上述查询将仅加载包含至少一个Address对象的User对象,该Address对象在其email字段中包含子字符串'aol.com'"。 - undefined
@PeterPetrus 不要在 filter() 中放置子项的条件,否则不符合条件的行将被排除,这样就会导致没有符合条件的子项的父项也被排除。如果你想保留没有符合条件的子项的父项,请将限制子项的条件放在 ON 子句中。 - undefined

-1

我不确定这是否是您要寻找的,但这种简单直接的方法有什么问题吗?

Post.query.filter(Post.user == <username>).filter(
    Post.time > START_OF_DAY).filter(Post.time < END_OF_DAY).all()

这里返回了一组 Post 对象。我想检索 User 对象,但我希望它们嵌入的 Post 集合(User.post)根据某些标准(时间戳)过滤出一组对象。 - melchoir55
啊,我明白了。你需要这个的实际情况是什么?我可以想象有其他方法可以实现你的目标(虽然从技术角度来看这是一个有趣的问题)。 - F. Pareto
创建计算效率高的检索信息方法。检索数据,然后更改其数据结构通常比直接检索您首先需要的数据结构不够高效。 - melchoir55

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接