首页 > 解决方案 > SqlAlchemy 为每个新组合自动增加复合键

问题描述

我有一个场景来迭代session_number相关的列user_name。如果用户在我将迭代最后一个会话之前创建了一个会话,session_number但如果用户第一次创建会话session_number应该从 1 开始。我试图在下面进行说明。现在我通过使用逻辑来处理这个问题,但尝试在 SqlAlchemy 中找到更优雅的方法来做到这一点。

id - user_name - session_number
1      user_1             1
2      user_1             2
3      user_2             1
4      user_1             3
5      user_2             2

这是我的表格的python代码。我的数据库是 PostgreSQL,我正在使用 alembic 来升级表。现在它继续迭代上session_number不管user_name

class UserSessions(db.Model):
    __tablename__ = 'user_sessions'

    id = db.Column(db.Integer, primary_key=True, unique=True)
    username = db.Column(db.String, nullable=False)
    session_number = db.Column(db.Integer, Sequence('session_number_seq', start=0, increment=1))
    created_at = db.Column(db.DateTime)
    last_edit = db.Column(db.DateTime)

    __table_args__ = (
        db.UniqueConstraint('username', 'session_number', name='_username_session_number_idx_'),
    )

我已经在互联网上搜索过这种情况,但那些不是我的问题。是否可以通过 SqlAlchemy/PostgreSQL 操作来实现这一点?

标签: pythonpostgresqlsqlalchemyalembic

解决方案


首先,我不知道通过使用 SqlAlchemy 或 Postgresql 或两者的组合来解决这种情况的任何“纯”解决方案。

虽然它可能不是您正在寻找的解决方案,但我希望它能给您一些想法。

如果您想计算session_number整个表而不存储它,我将使用以下查询或其变体:

def get_user_sessions_with_rank():
    expr = (
        db.func.rank()
        .over(partition_by=UserSessions.username, order_by=[UserSessions.id])
        .label("session_number")
    )
    subq = db.session.query(UserSessions.id, expr).subquery("subq")
    q = (
        db.session.query(UserSessions, subq.c.session_number)
        .join(subq, UserSessions.id == subq.c.id)
        .order_by(UserSessions.id)
    )
    return q.all()

或者,我实际上会column_property在模型中添加一个,为每个UserSessions. 它的计算效率不高,但对于按特定用户过滤的查询,它应该足够好:

class UserSessions(db.Model):
    __tablename__ = "user_sessions"

    id = db.Column(db.Integer, primary_key=True, unique=True)
    username = db.Column(db.String, nullable=False)
    created_at = db.Column(db.DateTime)
    last_edit = db.Column(db.DateTime)

# must define this outside of the model definition because of need for aliased
US2 = db.aliased(UserSessions)
UserSessions.session_number = db.column_property(
    db.select(db.func.count(US2.id))
    .where(US2.username == UserSessions.username)
    .where(US2.id <= UserSessions.id)
    .scalar_subquery()
)

在这种情况下,当您查询 时UserSessionssession_number将从数据库中获取 ,同时None用于新创建的实例。


推荐阅读