Python 如何在SQLAlchemy中对继承的列定义约束?
我有一个在中展示的类继承方案,我想定义一个约束,它使用父类和子类中的列Python 如何在SQLAlchemy中对继承的列定义约束?,python,sqlalchemy,Python,Sqlalchemy,我有一个在中展示的类继承方案,我想定义一个约束,它使用父类和子类中的列 from sqlalchemy import ( create_engine, Column, Integer, String, ForeignKey, CheckConstraint ) from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Parent(Base): __tab
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']
那么我如何定义这样的约束呢?简单回答:您不能使用CHECK约束来执行此操作 您不能在普通的RDBMS中执行此操作,因此您不能在使用SQLAlchemy中执行此操作。
但是,如果所有数据修改都通过应用程序(而不是直接访问数据库),则可以向类中添加验证例程:
class Child(Parent):
# ...
@validates('child_name')
def validate_child_name(self, key, child_name):
assert child_name != name
return child_name
阅读更多。经过一些修改后,我想出了一个解决方案:在
子
中创建一个“副本”parent\u name
列,引用parent
中的name
。它会浪费一些存储空间,但为了获得真正的检查约束
,这可能是不可避免的
代码如下:
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)
可以正常工作:
--------------------
{'type':'child','sa_instance_state':,'id':1,'child_name':'b','parent_name':'a','name':'a'}
{'type':'child','sa_instance_state':,'id':1,'child_name':'b','parent_name':'c','name':'c'}
由于IntegerityError而失败:
-------------------------------
关系“child”的新行违反了检查约束“child\u check”
详细信息:失败行包含(2,a,a)。
在子(id、父项名称、子项名称)中插入值(%(id)s、%(父项名称)s、%(子项名称)s)
我提出了一个解决方案/解决方法,我将其发布到了这个问题上。您认为该解决方案是否有任何不幸的副作用(性能等)?我不理解父项上的UniqueConstraint
的目的,因为它无论如何都不会被违反。您的解决方案基本上是信息的复制。我不确定这是一个积极的发展还是一个消极的发展。如果没有UniqueConstraint
,ForeignKeyConstraint
将无法工作。我同意最好不要重复,但我不认为有任何其他方法可以实现真正的CheckConstraint
子项名称
,您没有检查父项名称!=parent.name。那么回到第1步,你不认为吗?ForeignKeyConstraint
确保了parent\u name==parent.name
。你是对的。我没有意识到SA会考虑设置parent\u name
值,因为FK
。看起来你的解决方案很好,谢谢分享。