Python 如何构造接受最近插入的记录的SQLAlchemy关系?

Python 如何构造接受最近插入的记录的SQLAlchemy关系?,python,sqlalchemy,Python,Sqlalchemy,想象一下,我有以下几点: class User: id = Column(Integer, primary_key=True) username = Column(String(20), nullable=False) password_hash = Column(String(HASH_LENGTH), nullable=False) class LoginAttempts: id = Column(Integer, primary_key=True)

想象一下,我有以下几点:

class User:
    id = Column(Integer, primary_key=True)
    username = Column(String(20), nullable=False)
    password_hash = Column(String(HASH_LENGTH), nullable=False)


class LoginAttempts:
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey(User.id))
    attempted_at = Column(DateTime, default=datetime.datetime.utcnow)

现在,我想向
User
添加一个名为
last\u trunt
的关系,该关系检索最近的登录尝试。如何做到这一点呢?

这似乎是SQLAlchemy 1.3中添加的一个用例,在此之前,您会使用,或其他方法,如。其思想是创建一个子查询,表示每个用户最新登录尝试的派生表,然后将其别名为
loginatests
,并用作关系的目标。用于派生最新尝试的确切查询取决于DBMS1,但一般的左连接“antijoin”在大多数情况下都有效。首先为最新登录尝试生成(子)查询:

newer_attempts = aliased(LoginAttempts)

# This reads as "find login attempts for which no newer attempt with larger
# attempted_at exists". The same could be achieved using NOT EXISTS as well.
latest_login_attempts_query = select([LoginAttempts]).\
    select_from(
        outerjoin(LoginAttempts, newer_attempts,
                  and_(newer_attempts.user_id == LoginAttempts.user_id,
                       newer_attempts.attempted_at > LoginAttempts.attempted_at))).\
    where(newer_attempts.id == None).\
    alias()

latest_login_attempts = aliased(LoginAttempts, latest_login_attempts_query)
然后只需将关系属性添加到
用户
模型:

User.last_attempt = relationship(latest_login_attempts, uselist=False,
                                 viewonly=True)


1:例如,在Postgresql中,您可以使用横向子查询(不存在)、使用窗口函数的查询或
SELECT DISTINCT ON(user_id)。。。ORDER BY(用户id,在DESC尝试)

虽然选择的答案更可靠,但另一种方法是使用
lazy=dynamic
ORDER BY

User.last_attempted = relationship(LoginAttempts, order_by=desc(LoginAttempts.attempted_at), lazy='dynamic')
但是要小心,因为这将返回一个查询对象(并且需要
.first()
或等效项),并且需要使用limit子句:

last_attempted_login = session.query(User).get(my_user_id).last_attempted.limit(1).first()

相关的,但从那以后,大量的水已经流出:在模型定义期间创建会话的含义是什么?我们不希望根据需要生成特定于模型实例的查询吗?这一点很好,我本应该使用Core,而不是将子查询作为ORM查询来编写,它将更新。该语句不需要是特定于实例的。任何称职的DBMS都会推送谓词,并在需要时优化查询。但是,正如前面提到的,不同的DBMS提供不同的工具来处理“最新”的查询,并且它们可能会根据您的数据提供不同的性能。例如,在Postgresql中,您可以使用横向子查询或
DISTINCT ON。。。按
query订购。是的,有很多选择,谢谢更新!最后一个问题:无论如何要使这个
backref
友好吗?我认为这是不可能的。仔细想想,SQLAlchemy在加载
LoginAttests
时如何知道这是相关用户的最新登录尝试?事实上,
最后一次尝试
可能应该定义为
viewonly=True
。次要说明:没有必要设置
限制(1)
,因为“first()在生成的SQL(文档)中应用一个限制”