我有一个父Employee表和一个子Engineer表。从客户端的角度来看,我只想与Employee模型进行交互。这对于READ和DELETE很容易实现,但在尝试UPDATE或INSERT时会出现问题。
警告
目前,通常只能在层次结构中最基类的类上设置一个鉴别器列。 “Cascading”多态列尚不支持。
所以看起来默认这不会起作用。我正在寻找有关如何使这项工作的想法。
这是使用带有psycopg2的postgres的完整测试设置。 SQL可能适用于其他SQL数据库,但我测试了其他任何SQL数据库。
用于创建测试数据库(testdb)和表(员工,工程师)的SQL脚本:
CREATE DATABASE testdb;
\c testdb;
CREATE TABLE employee(
id INT PRIMARY KEY NOT NULL,
name TEXT,
type TEXT
);
CREATE TABLE engineer(
id INT PRIMARY KEY NOT NULL,
engineer_name TEXT,
employee_id INT REFERENCES employee(id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
Python测试脚本:
原样,INSERT测试将失败,但DELETE将通过。如果您更改代码(注释/取消注释)以使用子工程师模型,它将通过两种情况。
import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import (
Column,
ForeignKey,
Integer,
Text,
)
Base = declarative_base()
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(Text(), default='John')
type = Column(Text, default='engineer')
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type,
'with_polymorphic': '*',
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, ForeignKey('employee.id',
ondelete='CASCADE', onupdate='CASCADE'), primary_key=True)
engineer_name = Column(Text(), default='Eugine')
__mapper_args__ = {
'polymorphic_identity':'engineer',
}
def count(session, Model):
query = session.query(Model)
count = len(query.all())
return count
url = 'postgresql+psycopg2://postgres@localhost/testdb'
engine = sa.create_engine(url)
Base.metadata.bind = engine
Base.metadata.create_all()
Session = orm.sessionmaker(engine)
session = Session()
if __name__ == '__main__':
id=0
print '#'*30, 'INSERT', '#'*30
id += id
# I only want to interact with the Employee table
e = Employee(id=id)
# Use the child model to see the INSERT test pass
# e = Engineer(id=id)
session.add(e)
session.commit()
print 'pass' if count(session, Employee) == count(session, Engineer) else 'fail'
print '#'*30, 'DELETE', '#'*30
# e = session.query(Employee).first()
session.delete(e);
session.commit();
print 'pass' if count(session, Employee) == count(session, Engineer) else 'fail'
session.flush()
有关如何通过sqlalchemy模型定义实现此目的的任何想法,而不必使用显式控制器代码?
谢谢!
修改 好吧,我对这个没有任何爱。任何人都有关于如何使用控制器代码完成的想法?
答案 0 :(得分:0)
使用控制器逻辑可以通过getting the polymorphic subclass using the polymorphic identity完成。
我添加了两个函数来封装一些基本逻辑。
def get_polymorphic_class(klass, data):
column = klass.__mapper__.polymorphic_on
if column is None:
# The column is not polymorphic so the Class can be returned as-is
return klass
identity = data.get(column.name)
if not identity:
raise ValueError('Missing value for "' + column.name + '"', data)
mapper = klass.__mapper__.polymorphic_map.get(identity)
if mapper:
return mapper.class_
else:
raise ValueError('Missing polymorphic_identity definition for "' + identity + '"')
return klass
def insert(klass, data):
klass = get_polymorphic_class(klass, data)
e = klass(**data)
session.add(e)
session.commit()
return e
现在我更新main
以使用insert
功能,一切都按预期工作:
if __name__ == '__main__':
id=0
print '#'*30, 'INSERT', '#'*30
id += id
e = insert(Employee, {'id': id, 'type': 'engineer'})
print 'pass' if count(session, Employee) == count(session, Engineer) else 'fail'
print '#'*30, 'DELETE', '#'*30
session.delete(e);
session.commit();
print 'pass' if count(session, Employee) == count(session, Engineer) else 'fail'
session.flush()
我的封装中有一些额外的代码用于可重用性,但重要的是执行Employee.__mapper__.polymorphic_map['engineer'].class_
,返回Engineer
类,以便我们可以进行适当的级联INSERT。