我想在SQLAlchemy之上创建一个自定义接口,以便透明地支持一些预定义的混合属性。
具体来说,我想创建一个类SpecialColumn
和一个元类,以便当用户添加SpecialColumn
作为类的属性时,我的自定义元类用两个SQLAlchemy {{1}替换该属性并添加一个混合属性,将这两列作为元组获取并设置。
到目前为止,这是我的方法:
首先,我定义了我的特殊列类型:
Column
然后,我定义了一个继承自DeclarativeMeta的元类,它会在类中扫描class SpecialColumn(object):
pass
的实例,并用两个SpecialColumn
和一个混合属性(定义为闭包)替换它们:
Column
最后我用它构造了一个class MyDeclarativeMeta(DeclarativeMeta):
def __new__(cls, name, bases, attrs):
for name, col in attrs.items():
if isinstance(col, SpecialColumn):
# Replacing the column
del attrs[name]
col1_name = '_{0}_1'.format(name)
col2_name = '_{0}_2'.format(name)
attrs[col1_name] = Column(...)
attrs[col2_name] = Column(...)
# Adding the hybrid property
def getter(self):
return (getattr(self, col1_name), getattr(self, col2_name))
attrs[name] = hybrid_property(getter)
的实例,让用户用新的基类定义类:
declarative_base
现在我的问题: 首先,我的方法是否正确? 其次,我如何使用元类添加setter?这样做是否正确:
MyBase = declarative_base(metaclass=MyDeclarativeMeta)
class MyClass(MyBase):
col1 = SpecialColumn()
col2 = Column(...)
然后只需def setter(self, (v1, v2)):
setattr(self, col1_name, v1)
setattr(self, col2_name, v2)
?
答案 0 :(得分:7)
我们不需要为SQLAlchemy映射类使用元类,因为我们提供了大量的events来在创建和/或映射类时为类添加功能。 mapper_configured可能会很好,如果您使用0.8,则可以直接向MyBase
申请:
@event.listens_for(MyBase, 'mapper_configured')
def get_special_columns(mapper, cls):
for attrname in dir(cls):
val = getattr(cls, attrname)
if isinstance(val, SpecialColumn):
name1, name2 = "_%s_1" % attrname, "_%s_2" % attrname
setattr(cls, name1, Column(...))
setattr(cls, name2, Column(...))
@hybrid_property
def myhybrid(self):
return getattr(self, name1), getattr(self, name2)
@myhybrid.setter
def myhybrid(self, value):
setattr(self, name1, value[0])
setattr(self, name2, value[1])
setattr(cls, attrname, myhybrid)
请注意,setattr()是最好的方式,简单而重要。