自定义元类以在SQLAlchemy中创建混合属性

msk*_*kel 2 python sqlalchemy metaclass

我想在SQLAlchemy之上创建一个自定义接口,以便透明地支持一些预定义的混合属性.

具体来说,我想创建一个类SpecialColumn和一个元类,以便当用户添加SpecialColumn为类的属性时,我的自定义元类用两个SQLAlchemy替换该属性,Column并添加一个混合属性,获取并将这两列设置为元组.到目前为止,这是我的方法:

首先,我定义了我的特殊列类型:

class SpecialColumn(object):
     pass
Run Code Online (Sandbox Code Playgroud)

然后,我定义了一个继承自DeclarativeMeta的元类,它会扫描类的实例,SpecialColumn并用两个Columns和一个混合属性(定义为闭包)替换它们:

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)
Run Code Online (Sandbox Code Playgroud)

最后我declarative_base用它构造了一个实例,让用户使用新的基类定义类:

MyBase = declarative_base(metaclass=MyDeclarativeMeta)

class MyClass(MyBase):
    col1 = SpecialColumn()
    col2 = Column(...)
Run Code Online (Sandbox Code Playgroud)

现在我的问题:首先,我的方法是否正确?其次,我如何使用元类添加setter?这样做是否正确:

def setter(self, (v1, v2)):
    setattr(self, col1_name, v1)
    setattr(self, col2_name, v2)
Run Code Online (Sandbox Code Playgroud)

那么干脆attrs[name].setter(setter)呢?

zzz*_*eek 7

有没有必要为我们提供大量的使用元类的SQLAlchemy的映射类的事件,因为它们是创建和/或映射到功能添加到类. 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)
Run Code Online (Sandbox Code Playgroud)

请注意,setattr()是最好的方式,简单而重要.