In SQLAlchemy, I have read on how to combine the __table_args__
from different mixins when declaring a declarative.
Combining Table/Mapper Arguments from Multiple Mixins
The question I had is, the example shows how this is done at the end of the chain (final class in the MRO), but how would one achieve this say, if I had these Mixins and wanted it to occur in the MyClientMixin
or Base
class to avoid duplicating this code for other types of mixins?
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
I am struggling a bit with this concept.
This base class will search all mixins for __table_args__
to add, then check the current class for __local_table_args__
to add. This way, __local_table_args__
doesn't clash with the declared attr. The base classes (cls.mro()
) are checked in reverse order so that mixins lower down the chain are overridden by higher mixins.
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)
All your mixins should define __table_args__
as normal, but the "real" class inheriting from Base
should define __local_table_args__
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With