【问题标题】:SQLAlchemy Check Constraint With JoinSQLAlchemy 使用连接检查约束
【发布时间】:2018-02-11 23:01:35
【问题描述】:

我在 SQLAlchemy 中创建了多对多关系,使用如下:

b_c = Table('b_c', 
            Column('b_id', UUIDType(binary=False), ForeignKey('b.id'), primary_key=True),
            Column('c_id', UUIDType(binary=False), ForeignKey('c.id'), primary_key=True)
           )

其中cb 是只有id 列(UUIDType(binary=false)) 的表以及与此类似的模型:

class A(object):
    __tablename__ = 'a'
    id = Column('id', UUIDType(binary=False), default=uuid.uuid4, primary_key=True)

class B(object):
    __tablename__ = 'b'
    id = Column('id', UUIDType(binary=False), default=uuid.uuid4, primary_key=True)
    a_id = Column(UUIDType(binary=False), ForeignKey('a.id'), nullable=False)
    a = relationship('A')

class C(object):
    __tablename__ = 'c'
    id = Column('id', UUIDType(binary=False), default=uuid.uuid4, primary_key=True)
    a_id = Column(UUIDType(binary=False), ForeignKey('a.id'), nullable=False)
    a = relationship('A')

这种关系工作得非常好,我可以将 B 和 C 对象过滤到父 A 以用于我的使用场景。但是,为了确保使用这些模型的逻辑之外的数据完整性,是否有任何最佳实践要求任何关系 b_cb.a 必须等于 c.a

抱歉,如果这个问题得到了回答,但我发现的任何示例都是针对表本身值的简单 CHECK 约束,并且不需要连接表的值。

【问题讨论】:

  • 为什么A & C 子类object 而不是Model
  • 感谢您指出这一点,在尝试删除一些不必要的细节时错过了这一点 - 更新了问题

标签: python python-3.x postgresql sqlalchemy


【解决方案1】:

来自文档:

Currently, CHECK expressions cannot contain subqueries nor refer to variables other than columns of the current row. The system column tableoid may be referenced, but not any other system column.

您所描述的不能通过检查约束来完成,但可以在插入或更新之前通过 sql 触发器来实现:

这是一个 postgresql 函数和触发器定义,用于检查引用表的 a_id 外键是否相等。

CREATE FUNCTION ckref_b_c() RETURNS trigger AS $ckref_b_c$
  DECLARE
  bid uuid;
  cid uuid;
  BEGIN
    select a_id INTO bid FROM b WHERE id = NEW.b_id;
    select a_id INTO cid FROM c WHERE id = NEW.c_id;
    IF bid != cid THEN 
        RAISE EXCEPTION 'associated records do not refer to same parent in `a`';
    END IF;
    RETURN NEW;
  END;
 $ckref_b_c$ LANGUAGE plpgsql;

CREATE TRIGGER ckref_b_c BEFORE INSERT OR UPDATE ON b_c
  FOR EACH ROW EXECUTE PROCEDURE ckref_b_c(); 

您可以在创建表后通过 sqlalchemy 引擎执行这些查询。 Sqlalchemy 也有一个事件系统,您可以使用它来自动发出这些查询。

【讨论】:

  • 我想你已经搞定了。当我今天对此进行更多研究时,我确实觉得我正在走一条非常丑陋的道路,试图使用子查询执行 CHECK。正如我所发现的(你也在你的回答中提到),这在 postgres 中甚至是不允许的:stackoverflow.com/questions/10179121/… 这绝对是合适的解决方案,因为我发现几乎没有人推荐使用“hack”函数来制作约束。我通常会尽量避免触发,但在这里似乎很合适。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2016-02-27
  • 2018-01-14
  • 2013-12-07
  • 1970-01-01
相关资源
最近更新 更多