开发者

Dynamic Instance variables within Class Scope

开发者 https://www.devze.com 2023-02-27 03:44 出处:网络
I\'ve got a class that is a descendant of SQLAlchemy\'s declarative base. I need to write a bridge object that will translate between the declarative base and another system I am running, but I want t

I've got a class that is a descendant of SQLAlchemy's declarative base. I need to write a bridge object that will translate between the declarative base and another system I am running, but I want the bridge object to not have to know exactly what columns are in my record. That being the case, I want a way to list all of the columns in the record, to keep from having to maintain a list of attributes in two only vaguely related places, and to minimize replication in the meantime. This means I could create the column attributes, then maintain a separate list of column names, but again, replication is a factor--if I wanted to change the structure of this thing later, I'd have to change it in both places.

That being the case, I thought about sticking the开发者_如何学Go column definitions in a dict, and then iterating over the dict to create each column definitions with setattr, like so:

    for k,v in self.__column_dict.items():
        setattr(self,k,
                sqlalchemy.Column(v['column_type'],**v.get('opts',{})),
                )

Now, here's the rub: If I stick this bit of code on the class level, self is not yet defined, and I get an error (which makes sense). If I stick this in __init__, self is defined, but SQLAlchemy's declarative base gives an error because there is no primary key on the table by the time the class has finished being defined (which makes sense, since those attributes won't be set until runtime).

So, that all being said, how do I get what I want without using eval, or is using eval my only option, here?


EDIT: I accepted a solution below, but didn't use it 100% (although it was absolutely instrumental in getting me the answer). Here's what I ended up doing: (note that DeclarativeMeta is part of sqlalchemy.ext.declarative, the same package as declarative_base)

class MyMeta(DeclarativeMeta):
    def __new__(meta, classname, bases, dict_):
        klass = type.__new__(meta, classname, bases, dict_)
        if dict_.has_key('__classinit__'):
            klass.__classinit__ = staticmethod(klass.__classinit__.im_func)
        klass.__classinit__(klass, dict_)
        return klass

Then, in my derived class,

class DerivedClass(declarative_base()):
    __tablename__ = 'yaddayadda'
    __metaclass__ = MyMeta

    def __classinit__(klass, dict_):
        klass.__column_dict = <column dict here>

        for k,v in klass.__column_dict.items():
            setattr(klass,k,
                <fancy column stuff>
                )
   ...<rest of class definition>...


This can be done by providing a metaclass to declarative_base and in that metaclass

from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta

class MyMeta(DeclarativeMeta): 

    def __init__(klass, classname, bases, dict_): 

        for k, v in dict_.items():

            if k.endswith('__column_dict'):

                for name, datatype, is_pk in v:

                    setattr(klass, name, Column(name, datatype, primary_key=is_pk))

        return DeclarativeMeta.__init__(klass, classname, bases, dict_)

Base = declarative_base(metaclass=MyMeta)

class Bob(Base):
    __tablename__ = 'bob'
    __column_dict = [('a', Integer, True), ('b', Integer, False)]

bob = Bob()
print bob.a
print bob.b
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号