Динамические переменные экземпляра в пределах класса - PullRequest
1 голос
/ 20 апреля 2011

У меня есть класс, который является потомком декларативной базы SQLAlchemy.Мне нужно написать объект моста, который будет переводить между декларативной базой и другой системой, которую я использую, но я хочу, чтобы объект моста не знал точно, какие столбцы находятся в моей записи.В таком случае мне нужен способ составить список всех столбцов в записи, чтобы избежать необходимости поддерживать список атрибутов в двух только смутно связанных местах и ​​минимизировать репликацию между тем.Это означает, что я мог бы создать атрибуты столбца, а затем вести отдельный список имен столбцов, но опять же, репликация является фактором - если бы я хотел изменить структуру этой вещи позже, мне пришлось бы изменить ее в обоих местах.

В таком случае я подумал о том, чтобы вставить определения столбцов в dict, а затем перебрать их, чтобы создать определения каждого столбца с помощью setattr, например:

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

Теперь,вот в чем проблема: если я вставлю этот бит кода на уровне класса, self еще не определено, и я получаю ошибку (что имеет смысл).Если я вставлю это в __init__, самоопределение будет определено, но декларативная база SQLAlchemy выдает ошибку, поскольку к моменту окончания определения класса первичный ключ в таблице отсутствует (что имеет смысл, поскольку эти атрибуты не будутустановить до времени выполнения).

Итак, как все говорят, как получить то, что я хочу, без использования eval, или использовать eval - мой единственный вариант, здесь?


EDIT:Я принял решение, приведенное ниже, но не использовал его на 100% (хотя оно помогло мне получить ответ).Вот что я в итоге сделал: (обратите внимание, что DeclarativeMeta является частью sqlalchemy.ext.declarative, того же пакета, что и 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

Затем в моем производном классе

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>...

1 Ответ

1 голос
/ 21 апреля 2011

Это может быть сделано путем предоставления метакласса для Declarative_base и в этом метаклассе

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
...