在Flask-SQLalchemy中是否可以将布尔列设置为唯一约束?

时间:2018-10-25 03:06:54

标签: python flask sqlalchemy flask-sqlalchemy

是否可以通过设置表的方式在任何给定时间将一个记录设置为true?

我有一个“用户”表和一个“凭据”表。用户可以具有一个或多个与其关联的登录凭据。凭据表中“活动”列(布尔值)指示的一次只能有一个“活动”凭据。

是否可以设置某种约束来防止两个记录同时处于“活动状态”?

2 个答案:

答案 0 :(得分:0)

因此,如果我正确阅读,则有两个模型类UserCredential,这是一对多关系:

class User(db.Model):
    ...
    credentials = db.relationship('Credential')

class Credential(db.Model):
    ...
    user_id = db.Column(db.Integer, db.ForeignKey('user.id', nullable=False)

是否可以添加其他外键来表示一对一关系:

class User(db.Model):
    ...
    active_credential_id = db.Column(db.Integer, db.ForeignKey('credential.id'))

class Credential(db.Model):
    ...
    active_user = db.relationship('User')

您可以通过以下方式更新它:

inactive_credential = # some credential from the user's list of credentials
user = User.query.filter(User.id == inactive_credential.user_id)
user.active_credential_id = inactive_credential.id
db.session.add(user)
db.session.commit()
# inactive_credential ==>> active_credential

此处使用外键可维护数据库的完整性。

您将需要一些附加约束,说一个active_credential_id只能从其用户由user_id定义的凭据列表中选择。我只是在最后想到了这一点,如果有解决办法,稍后会更新答案。

答案 1 :(得分:0)

您可以使用唯一约束来防止同时激活2列,但是必须使用True和None作为可能的值,而不是True和False。这是因为唯一约束将仅在列中允许单个True,但也仅允许单个False。没有一个值(或SQL NULL)不参与唯一约束,因此,您可以拥有其余行而拥有尽可能多的这样的值。为了确保数据库的完整性,最好使用仅具有一个可能值的枚举数据类型来实现。

import enum

class Active(enum.Enum):
    true = True

class Credentials(db.Model):
    active = db.Column(db.Enum(Active), unique=True)

现在,您可以使用Active.true作为指示活动凭据的值,而对于在数据库级别强制执行完整性的所有其他凭据,则使用None。如果您希望每个用户拥有一个活动凭证,而不是总共拥有一个活动凭证,则可以使用单独的UniqueConstraint语句来实现。

class Credential(db.Model):
    __tablename__ = "credentials"
    __table_args__ = (db.UniqueConstraint('user_id', 'active'),)

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    user = db.relationship("User", back_populates="credentials")
    active = db.Column(db.Enum(Active))

class User(db.Model):
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    credentials = db.relationship("Credential", back_populates="user")

虽然这样做确实可以防止将多个凭证标记为活动凭证,但这不会阻止用户没有活动凭证,因此您需要依赖于应用程序逻辑。