如何构造一个采用最近插入的记录的SQLAlchemy关系?

时间:2019-08-02 23:20:00

标签: 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)
    user_id = Column(Integer, ForeignKey(User.id))
    attempted_at = Column(DateTime, default=datetime.datetime.utcnow)

现在,我想向User添加一个名为last_attempt的关系,以检索最近的登录尝试。怎么可能呢?

2 个答案:

答案 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=dynamicorder_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()