【问题标题】:SQLAlchemy: Any constraint to check one of the two columns is not null?SQLAlchemy:检查两列之一的任何约束不为空?
【发布时间】:2014-01-23 11:47:51
【问题描述】:

这可能是完全愚蠢的问题,但我的模型中有这样的要求,至少categoryparent_categorynot null

我的模型看起来像

class BudgetCategories(db.Model):
    __tablename__ = 'budget_categories'
    uuid = Column('uuid', GUID(), default=uuid.uuid4, primary_key=True,
                  unique=True)
    budget_id = Column(GUID(), ForeignKey('budgets.uuid'), nullable=False)
    budget = relationship('Budget', backref='budgetCategories')
    category = Column('category', sa.types.String, nullable=True)
    parent_category = Column('parent_category', sa.types.String, nullable=True)
    amount = Column('amount', Numeric(10, 2), nullable=False)
    recurring = Column('recurring', sa.types.Boolean,
                       nullable=False)
    created_on = Column('created_on', sa.types.DateTime(timezone=True),
                        nullable=False)

我该如何指定。我什至不知道该尝试什么

欢迎指点

我使用PostgreSQL作为后端数据库

【问题讨论】:

  • categoryparent_category 中至少有一个是not null 或恰好其中一个是not null
  • 至少。如果两者都已填充-> 好,但至少 category 或 parent_category 不为空。我希望澄清
  • a category is not null or parent_category is not null CHECK 约束会在数据库级别执行此操作,但不确定 Python 级别。

标签: python postgresql sqlalchemy flask-sqlalchemy


【解决方案1】:

我不是 100% 确定 PostgreSQL 语法,但是在您的 BudgetCategories 模型中添加之后应该使用 CheckConstraint 来解决问题:

class BudgetCategories(Base):
    __tablename__ = 'budget_categories'
    # ...

    # @note: new
    __table_args__ = (
            CheckConstraint('NOT(category IS NULL AND parent_category IS NULL)'),
            )

【讨论】:

  • 也许你可以帮助我。如果您有超过 2 个字段并且其中只有一个应该有值,而其余的应该为空,那该怎么办?谢谢
  • @ChickenFeet:考虑thisthis 答案
【解决方案2】:

我的 SQLalchemy 模型中需要 XOR 行为。我想出了以下定义(使用的后端:PostgreSQL):

from sqlalchemy.schema import (
    CheckConstraint
)

class ScheduledNotebook(Base):
    __table_args__ = (
        (CheckConstraint('(uuid::text IS NULL) <> (notebook_path IS NULL)', name='uuid_xor_notebook_path')),
    )

    id = Column(Integer, primary_key=True)
    notebook_path = Column(String, nullable=True, unique=True)
    uuid = Column(UUID(as_uuid=True), primary_key=True, unique=True, nullable=True)

并遵循 alembic 迁移(注意:自动生成不会检测到它 - 您必须手动添加它):

def upgrade():
    op.create_check_constraint(
        'uuid_xor_notebook_path',
        table_name='scheduled_notebooks',
        schema='metadata',
        condition='(uuid::text IS NULL) <> (notebook_path IS NULL)'
    )


def downgrade():
    op.drop_constraint('uuid_xor_notebook_path')

它就像一个魅力:

- 只有 notebook_path - 好的

datalake=#  INSERT INTO scheduled_notebooks (schedule,enabled,owner, notebook_path) VALUES ('{"kind":"hourly"}',true,'akos', '/a/b/c/d/e.ipynb');
INSERT 0 1

- 只有 uuid - 好的

datalake=#  INSERT INTO scheduled_notebooks (schedule,enabled,owner, uuid) VALUES ('{"kind":"hourly"}',true,'akos', '7792bd5f-5819-45bf-8902-8cf43102434d');
INSERT 0 1

- uuid 和 notebook_path - 根据需要失败

datalake=#  INSERT INTO scheduled_notebooks (schedule,enabled,owner, uuid, notebook_path) VALUES ('{"kind":"hourly"}',true,'akos', '7792bd5f-5819-45bf-8902-8cf43102434f', '/a/b/c/d');
ERROR:  new row for relation "scheduled_notebooks" violates check constraint "uuid_xor_notebook_path"
DETAIL:  Failing row contains (567, /a/b/c/d, {"kind": "hourly"}, t, akos, null, null, null, 7792bd5f-5819-45bf-8902-8cf43102434f).

- 既不是 uuid 也不是 notebook_path - 根据需要失败

datalake=#  INSERT INTO scheduled_notebooks (schedule,enabled,owner) VALUES ('{"kind":"hourly"}',true,'akos');
ERROR:  new row for relation "scheduled_notebooks" violates check constraint "uuid_xor_notebook_path"
DETAIL:  Failing row contains (568, null, {"kind": "hourly"}, t, akos, null, null, null, null).

【讨论】:

    【解决方案3】:

    我希望还不算太晚,但这应该可以解决问题,并检查它是否可以与 PostGreSQL DB 一起使用:

    class BudgetCategories(Base):
        __tablename__ = 'budget_categories'
        __table_args__ = (
            CheckConstraint('coalesce(category , parent_category ) is not null'),
        )
        # ...
    

    【讨论】:

    • 谁能解释为什么它被否决了?是因为我不知道的一些性能影响吗?也许数据库后端支持?我真的很好奇,因为我个人觉得这个解决方案很不错。
    • 我很想知道@radzak。也许是因为阅读起来不太明显?非常值得商榷。我不能说它有问题。
    【解决方案4】:

    在使用 PostgreSQL 后端时,您也可以为此使用num_nonnulls 函数:

    class BudgetCategories(Base):
        __tablename__ = 'budget_categories'
        __table_args__ = (
            CheckConstraint('num_nonnulls(category, parent_category) = 1'),
        )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-27
      • 1970-01-01
      • 2014-11-23
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      相关资源
      最近更新 更多