自定义元类以在SQLAlchemy中创建混合属性 [英] Custom metaclass to create hybrid properties in SQLAlchemy
问题描述
我想在SQLAlchemy的顶部创建一个自定义接口,以便透明地支持一些预定义的混合属性.
I want to create a custom interface on top of SQLAlchemy so that some pre-defined hybrid properties are supported transparently.
具体来说,我想创建一个类SpecialColumn
和一个元类,以便当用户将SpecialColumn
添加为类的属性时,我的自定义元类用两个SQLAlchemy Column
替换了该属性,并添加了一个混合类.属性,该属性获取并将这两列设置为元组.
到目前为止,这是我的方法:
Specifically, I want to create a class SpecialColumn
and a metaclass so that when a user adds SpecialColumn
as an attribute of a class, my custom metaclass replaces that attribute with two SQLAlchemy Column
s and adds a hybrid property that gets and sets those two columns as a tuple.
Here's my approach so far:
首先,我定义了特殊的列类型:
First, I defined my special column type:
class SpecialColumn(object):
pass
然后,我定义了一个从DeclarativeMeta继承的元类,该元类在类中扫描SpecialColumn
的实例,并将其替换为两个Column
和一个混合属性(定义为闭包):
Then, I defined a metaclass inheriting from DeclarativeMeta which scans the class for instances of SpecialColumn
and replaces them with two Column
s and a hybrid property (defined as a closure):
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
的实例,并让用户使用新的基础定义类:
And finally I constructed an instance of declarative_base
with it, and let the user define classes with the new base:
MyBase = declarative_base(metaclass=MyDeclarativeMeta)
class MyClass(MyBase):
col1 = SpecialColumn()
col2 = Column(...)
现在我的问题是: 首先,我的方法正确吗? 其次,如何使用元类添加设置器?这样做正确吗?
Now for my questions: Firstly, is my approach correct? Secondly, how can I use the metaclass to add the setter? Would it be correct to do:
def setter(self, (v1, v2)):
setattr(self, col1_name, v1)
setattr(self, col2_name, v2)
然后只需执行attrs[name].setter(setter)
吗?
推荐答案
由于我们提供了大量 mapper_configured 在这里可能会很好,如果您在0.8上,您可以直接申请MyBase
:
There's no need to use metaclasses for a SQLAlchemy mapped class as we supply plenty of events to add features to classes as they are created and/or mapped. mapper_configured might be good here, which if you're on 0.8 you can apply to MyBase
directly:
@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()是到达此处的最佳方法,简单明了.
note that setattr() is the best way to go here, simple and to the point.
这篇关于自定义元类以在SQLAlchemy中创建混合属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!