在SQLAlchemy中将__table_args__与mixin类的约束结合
在SQLAlchemy中,我看到有关于如何在声明一个声明式类时,结合来自不同混合类的__table_args__
的内容。
我有个问题,例子展示了如何在链的末尾(MRO中的最终类)完成这个操作,但如果我有这些混合类,想要在MyClientMixin
或Base
类中实现这个功能,以避免为其他类型的混合类重复这段代码,该怎么做呢?
class LaneCarrierCommitmentSummaryMixin(object):
""" Lane Carrier Commitment Summary.
A base class for LCCS to mixin with a client specific class.
"""
__tablename__ = 'lane_carrier_commitment_summary'
__table_args__ = ((UniqueConstraint(['hashmap_key', 'bow'],
name='uq_lane_carrier_commitment_summary_hashmap_key_bow')),)
class MyClientMixin(object):
""" MyClient Mixin class for providing the ETL schema. """
__table_args__ = {'schema': 'myclient_etl'}
class MyClientLaneCarrierCommitmentSummary(LaneCarrierCommitmentSummaryMixin, DateTrackedMixin, MyClientMixin, Base):
pass
我对这个概念有点困惑。
1 个回答
13
这个基础类会去查找所有的混合类(mixins)里面有没有__table_args__
,然后把找到的东西加上。接着,它还会检查当前的类里有没有__local_table_args__
,如果有的话也会加上。这样做的好处是,__local_table_args__
就不会和已经声明的属性冲突。基础类(cls.mro()
)会按照相反的顺序检查,这样在继承链中,靠后的混合类会被靠前的混合类覆盖。
def _process_args(cls, attr, out_args, out_kwargs):
try:
args = getattr(cls, attr)
except AttributeError:
return
if isinstance(args, Mapping): # it's a dictionary
out_kwargs.update(args)
else: # it's a list
if isinstance(args[-1], Mapping): # it has a dictionary at the end
out_kwargs.update(args.pop())
out_args.extend(args)
class Base():
@declared_attr
def __mapper_args__(cls):
args = []
kwargs = {}
for mixin in reversed(cls.mro()):
_process_args(mixin, '__mapper_args__', args, kwargs)
_process_args(mixin, '__local_mapper_args__', args, kwargs)
return kwargs # mapper only takes dict
@declared_attr
def __table_args__(cls):
args = []
kwargs = {}
for mixin in reversed(cls.mro()):
_process_args(mixin, '__table_args__', args, kwargs)
_process_args(cls, '__local_table_args__', args, kwargs)
args.append(kwargs) # [item, item, ..., kwargs]
return tuple(args)
你所有的混合类都应该像往常一样定义__table_args__
,但是从Base
继承的“真实”类应该定义__local_table_args__
。