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

Posted

技术标签:

【中文标题】在 SQLAlchemy 中创建混合属性的自定义元类【英文标题】:Custom metaclass to create hybrid properties in SQLAlchemy 【发布时间】:2012-12-14 05:21:13 【问题描述】:

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

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

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

class SpecialColumn(object):
     pass

然后,我定义了一个继承自 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)

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

MyBase = declarative_base(metaclass=MyDeclarativeMeta)

class MyClass(MyBase):
    col1 = SpecialColumn()
    col2 = Column(...)

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

def setter(self, (v1, v2)):
    setattr(self, col1_name, v1)
    setattr(self, col2_name, v2)

然后简单地做attrs[name].setter(setter) ?

【问题讨论】:

【参考方案1】:

没有必要为 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() 是最好的方法,简单明了。

【讨论】:

感谢您的回复以及 SQLAlchemy!

以上是关于在 SQLAlchemy 中创建混合属性的自定义元类的主要内容,如果未能解决你的问题,请参考以下文章

我在 ActiveDirectory 中创建了一个新的自定义属性。如何在 PowerShell 中修改它?

如何在 SQLAlchemy 1.2.7 中创建没有定义类的新表

如何在 C# 中创建自定义属性

在 SwiftUI 中创建一个包含自定义类数组的列表

带有在 IB 中创建的自定义视图的 InputAccessoryView

如何在颤动中创建具有圆形边缘的自定义容器?