我有一个类继承方案,如http://docs.sqlalchemy.org/en/latest/orm/inheritance.html#joined-table-inheritance所示,我想定义一个使用父类和子类列的约束。
from sqlalchemy import (
create_engine, Column, Integer, String, ForeignKey, CheckConstraint
)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
type = Column(String)
name = Column(String)
__mapper_args__ = {'polymorphic_on': type}
class Child(Parent):
__tablename__ = 'child'
id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
child_name = Column(String)
__mapper_args__ = {'polymorphic_identity': 'child'}
__table_args__ = (CheckConstraint('name != child_name'),)
engine = create_engine(...)
Base.metadata.create_all(engine)
这不起作用,因为name
不是child
中的列;我收到了错误
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) column "name" does not exist
[SQL: '\nCREATE TABLE child (\n\tid INTEGER NOT NULL, \n\tPRIMARY KEY (id), \n\tCHECK (name="something"), \n\tFOREIGN KEY(id) REFERENCES parent (id)\n)\n\n']
那么如何定义这样的约束呢?
答案 0 :(得分:2)
简单回答:你不能使用CHECK Constraint执行此操作。
您不能在普通的RDBMS中执行此操作,因此您无法使用SQLAlchemy 但是,如果所有数据修改都通过您的应用程序(而不是通过直接数据库访问),您可以向类中添加验证例程:
class Child(Parent):
# ...
@validates('child_name')
def validate_child_name(self, key, child_name):
assert child_name != name
return child_name
了解更多Simple Validators。
答案 1 :(得分:1)
经过一些修补后,我提出了一个解决方案:在parent_name
中创建一个引用Child
中name
的“复制”Parent
列。它浪费了一些存储空间,但为了拥有真正的CHECK CONSTRAINT
,这可能是不可避免的。
以下是代码:
from sqlalchemy import (
create_engine, Column, Integer, String,
CheckConstraint, UniqueConstraint, ForeignKeyConstraint
)
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import IntegrityError
Base = declarative_base(metadata=metadata)
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
type = Column(String, nullable=False)
name = Column(String, nullable=False)
__mapper_args__ = {'polymorphic_on': type}
__table_args__ = (UniqueConstraint('id', 'name'),)
class Child(Parent):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_name = Column(String, nullable=False)
child_name = Column(String, nullable=False)
__mapper_args__ = {'polymorphic_identity': 'child'}
__table_args__ = (
ForeignKeyConstraint(
['id', 'parent_name'], ['parent.id', 'parent.name'],
onupdate='CASCADE', ondelete='CASCADE'
),
CheckConstraint('parent_name != child_name'),
)
engine = create_engine(...)
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine, autocommit=True)()
print('Works without error:')
print('--------------------')
with session.begin():
session.add(Child(name='a', child_name='b'))
print(session.query(Child).one().__dict__)
with session.begin():
child = session.query(Child).one()
child.name = 'c'
print(session.query(Child).one().__dict__)
print('\nFails due to IntegerityError:')
print('-------------------------------')
try:
with session.begin():
session.add(Child(name='a', child_name='a'))
except IntegrityError as e:
print(e.orig)
print(e.statement)
此脚本的输出是
Works without error:
--------------------
{'type': 'child', '_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f5e9b9b7898>, 'id': 1, 'child_name': 'b', 'parent_name': 'a', 'name': 'a'}
{'type': 'child', '_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f8fc80f2b38>, 'id': 1, 'child_name': 'b', 'parent_name': 'c', 'name': 'c'}
Fails due to IntegerityError:
-------------------------------
new row for relation "child" violates check constraint "child_check"
DETAIL: Failing row contains (2, a, a).
INSERT INTO child (id, parent_name, child_name) VALUES (%(id)s, %(parent_name)s, %(child_name)s)