我正在使用SQLAlchemy的单表继承Transaction
,StudentTransaction
和CompanyTransaction
:
class Transaction(Base):
__tablename__ = 'transaction'
id = Column(Integer, primary_key=True)
# Who paid? This depends on whether the Transaction is a
# CompanyTransaction or a StudentTransaction. We use
# SQLAlchemy's Single Table Inheritance to make this work.
discriminator = Column('origin', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
# When?
time = Column(DateTime, default=datetime.utcnow)
# Who administered it?
staff_id = Column(Integer, ForeignKey('staff.id'))
staff = relationship(
'Staff',
primaryjoin='and_(Transaction.staff_id==Staff.id)'
)
# How much?
amount = Column(Integer) # Negative for refunds, includes the decimal part
# Type of transaction
type = Column(Enum(
'cash',
'card',
'transfer'
))
class CompanyTransaction(Transaction):
__mapper_args__ = {'polymorphic_identity': 'company'}
company_id = Column(Integer, ForeignKey('company.id'))
company = relationship(
'Company',
primaryjoin='and_(CompanyTransaction.company_id==Company.id)'
)
class StudentTransaction(Transaction):
__mapper_args__ = {'polymorphic_identity': 'student'}
student_id = Column(Integer, ForeignKey('student.id'))
student = relationship(
'Student',
primaryjoin='and_(StudentTransaction.student_id==Student.id)'
)
然后,我有一个学生,它定义了与StudentTransactions的一对多关系:
class Student(Base):
__tablename__ = 'student'
id = Column(Integer, primary_key=True)
transactions = relationship(
'StudentTransaction',
primaryjoin='and_(Student.id==StudentTransaction.student_id)',
back_populates='student'
)
@hybrid_property
def balance(self):
return sum([transaction.amount for transaction in self.transactions])
问题是,为NotImplementedError: <built-in function getitem>
函数中的返回行调用学生收益:Student.balance()
。
我做错了什么?
感谢。
答案 0 :(得分:7)
hybrid property是一种允许生成Python描述符的构造,该描述符在实例级别以一种方式运行,而在类级别以另一种方式运行。在类级别,我们希望它生成一个SQL表达式。使用像sum()
这样的纯Python函数或列表推导来生成SQL表达式是不合法的。
在这种情况下,如果我从“学生”表中查询并且我希望在“事务”表中生成“金额”列的总和,我可能希望将相关子查询与SQL一起使用集合函数。我们希望看到的SQL类似于:
SELECT * FROM student WHERE (
SELECT SUM(amount) FROM transaction WHERE student_id=student.id) > 500
我们的混合动力必须控制并产生这种表达:
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
Base = declarative_base()
class Transaction(Base):
__tablename__ = 'transaction'
id = Column(Integer, primary_key=True)
discriminator = Column('origin', String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
amount = Column(Integer)
class StudentTransaction(Transaction):
__mapper_args__ = {'polymorphic_identity': 'student'}
student_id = Column(Integer, ForeignKey('student.id'))
student = relationship(
'Student',
primaryjoin='and_(StudentTransaction.student_id==Student.id)'
)
class Student(Base):
__tablename__ = 'student'
id = Column(Integer, primary_key=True)
transactions = relationship(
'StudentTransaction',
primaryjoin='and_(Student.id==StudentTransaction.student_id)',
back_populates='student'
)
@hybrid_property
def balance(self):
return sum([transaction.amount for transaction in self.transactions])
@balance.expression
def balance(cls):
return select([
func.sum(StudentTransaction.amount)
]).where(StudentTransaction.student_id==cls.id).as_scalar()
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)
s.add_all([
Student(transactions=[StudentTransaction(amount=50), StudentTransaction(amount=180)]),
Student(transactions=[StudentTransaction(amount=600), StudentTransaction(amount=180)]),
Student(transactions=[StudentTransaction(amount=25), StudentTransaction(amount=400)]),
])
print s.query(Student).filter(Student.balance > 400).all()
最后的输出:
SELECT student.id AS student_id
FROM student
WHERE (SELECT sum("transaction".amount) AS sum_1
FROM "transaction"
WHERE "transaction".student_id = student.id) > ?
2014-04-19 19:38:10,866 INFO sqlalchemy.engine.base.Engine (400,)
[<__main__.Student object at 0x101f2e4d0>, <__main__.Student object at 0x101f2e6d0>]