據我所知/在我的經驗/按本StackOveflow答案:https://stackoverflow.com/a/10612690/3329834你不能像這樣與ORM工會。
我設法實現你要找的人(更多或更少)的語法和背部加載到一切的回報ORM。關於工會(相同的列數等)的正常注意事項都適用於這裏更多(需要過濾相同的列名稱)。另外,我不認爲我會永遠在實踐中使用這個....
from functools import partial
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import *
from sqlalchemy import orm
from sqlalchemy import sql
engine = sqlalchemy.create_engine('sqlite://')
connection = engine.connect()
Base = declarative_base()
class Student(Base):
__tablename__ = "students"
id = Column(Integer, primary_key=True)
name = Column(String(767), unique=True)
caretaker = Column(String(50))
def __repr__(self):
return 'Student(name={s.name}, caretaker={s.caretaker}'.format(s=self)
class Patient(Base):
__tablename__ = "patients"
id = Column(Integer, primary_key=True)
name = Column(String(767), unique=True)
caretaker = Column(String(50))
def __repr__(self):
return 'Patient(name={s.name}, caretaker={s.caretaker}'.format(s=self)
class StagedOperation(object):
def __init__(self, attr):
self.attr = attr
def __call__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
class StagedQuery(object):
def __init__(self, model, session=None):
self.session = session
self.models = [model]
self.columns = [e.name for e in model.__table__.columns]
self.ops = []
def __getattr__(self, attr):
# __getattr__ fires only when an attribute is requested & not found
# We will attempt to pass on any attribute call on to the resulting
# Query objects; do note this will only work, technically and logicaly,
# with method calls, not attribute access
if hasattr(orm.query.Query, attr):
obj = StagedOperation(attr)
self.ops.append(obj)
# really getting hacky to enable "chaining"
# Could also build this into the StagedOperation.__call__
def _allow_chaining(desired_return, op, *args, **kwargs):
op(*args, **kwargs)
return desired_return
return partial(_allow_chaining, self, obj)
def with_unions(self, *models):
self.models.extend(models)
return self
def with_session(self, session):
self.session = session
return self
def query(self):
q = None
for model in self.models:
id_col = sql.literal(model.__tablename__).label('tablename')
columns = self.columns + [id_col]
mq = orm.query.Query(columns).select_from(model)
for op in self.ops:
mq = getattr(mq, op.attr)(*op.args, **op.kwargs)
q = q.union(mq) if q else mq
return q
def _deserialize_row(self, row):
ref = {e.__tablename__: e for e in self.models}
return ref[row.tablename](**{k: getattr(row, k) for k in self.columns})
def one(self):
return self._deserialize_row(
self.query().with_session(self.session).one())
def first(self):
r = self.query().with_session(self.session).first()
if r:
return self._deserialize_row(r)
def all(self):
return [
self._deserialize_row(e) for e in
self.query().with_session(self.session).all()]
if __name__ == '__main__':
engine = create_engine('sqlite://')
Session = orm.sessionmaker()
Session.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all()
session = Session()
#
# Insert some objects
#
stu = Student(id=1, name='John', caretaker='Mother')
stu2 = Student(id=2, name='Sally', caretaker='Mother')
stu3 = Student(id=3, name='Scott', caretaker='Father')
pat = Patient(id=1, name='Susan', caretaker='Mother')
pat2 = Patient(id=2, name='Sally', caretaker='Father')
pat3 = Patient(id=3, name='Turnip', caretaker='Father')
session.add_all([stu, stu2, stu3, pat, pat2, pat3])
session.flush()
# Some usage options
print (
StagedQuery(Student)
.filter_by(caretaker='Mother')
.with_unions(Patient)
.with_session(session)
.all())
print (
StagedQuery(Student, session=session)
.filter_by(caretaker='Mother')
.filter_by(name='Sally')
.with_unions(Patient)
.all())
打印...
[Student(name=John, caretaker=Mother, Patient(name=Susan, caretaker=Mother, Student(name=Sally, caretaker=Mother]
[Student(name=Sally, caretaker=Mother]
並不可怕,但可用的還遠遠。它給了我一些想法,謝謝。順便說一下,當你有多態的身份時,工會就會工作。事實上,如果我使用基類進行查詢,SQLAlchemy會自動執行聯合,但是他將where子句放在最終的聯合查詢中而不是每個子查詢中,而且MySQL對於優化這個問題太愚蠢了。 – 2014-10-19 00:06:02
是的,當你有一個多態的身份時,工會可以工作;我會在你的問題中加上這一點,它會改變我的答案。 – Jason 2014-10-20 12:39:17
好主意。我剛剛做完。 – 2014-10-20 13:32:38