想象一下我有以下内容:
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_attempt
的关系,以检索最近的登录尝试。怎么可能呢?
答案 0 :(得分:1)
这似乎是a relationship to an aliased class的一个用例,它是在SQLAlchemy 1.3中添加的–在您使用non primary mapper或诸如custom primary join之类的其他方法之前。这个想法是创建一个子查询,代表每个用户最新登录尝试的派生表,然后将其别名为LoginAttempts
并用作关系的目标。用于得出最新尝试的确切查询取决于您的DBMS 1 ,但是大多数情况下,通用的左联接“ 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
模型中:
User.last_attempt = relationship(latest_login_attempts, uselist=False,
viewonly=True)
1:例如,在Postgresql中,您可以将LEFT JOIN子查询替换为LATERAL子查询,NOT EXISTS,使用窗口函数的查询或SELECT DISTINCT ON (user_id) ... ORDER BY (user_id, attempted_at DESC)
。
答案 1 :(得分:0)
尽管所选答案更可靠,但您可以使用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()