2013-08-20 2 views
2

Я создаю backend-приложение с SQLAlchemy, используя декларативную базу. ORM требует около 15 таблиц, каждая из которых сопоставляет объект класса в SQLAlchemy. Поскольку эти объекты класса все определены одинаково, я думал, что заводская модель может сделать классы более сжатыми. Однако эти классы не только должны быть определены, они должны быть назначены уникальным именам переменных, чтобы их можно было импортировать и использовать в проекте.Определение класса динамического класса Python в SQLAlchemy

(Извините, если этот вопрос немного долго, я обновил его, как я лучше понял проблему.)

Потому что у нас есть так много столбцов (~ 1000) мы определяем их имена и тип внешних текстовых файлов в сохраняйте читаемость. Сделав это один из способов идти о признании наших моделей, как это:

class Foo1(Base): 
    __tablename___ = 'foo1' 

class Foo2(Base): 
    __tablename___ = 'foo2' 

... etc 

, а затем я могу добавить столбцы, обернув над содержимым внешнего текстового файла и с помощью setattr() на каждом определении класса.

Это нормально, но это кажется слишком повторяющимся, поскольку у нас около 15 таблиц. Поэтому вместо этого я принял удар по написанию фабричной функции, которая могла бы динамически определять классы.

def orm_factory(class_name): 
    class NewClass(Base): 
     __tablename__ = class_name.lower() 
    NewClass.__name__ = class_name.upper() 
    return NewClass 

Опять же, я могу просто цикл по столбцам и использовать setattr(). Когда я положил его вместе это выглядит следующим образом:

for class_name in class_name_list: 
    ORMClass = orm_factory(class_name) 
    header_keyword_list = get_header_keyword_list(class_name) 
    define_columns(ORMClass, header_keyword_list) 

Где get_header_keyword_list получает информацию столбца и define_columns выполняет setattr() назначения. Когда я использую это и запускаю Base.metadata.create_all(), схема SQL получается сгенерированной просто отлично.

Но, когда я потом пытаюсь импортировать эти определения классов в другую модель я получаю сообщение об ошибке, как это:

SAWarning: The classname 'NewClass' is already in the registry of this declarative base, mapped to <class 'ql_database_interface.IR_FLT_0'> 

Это теперь я понимаю, имеет полный смысл, основанный на том, что я узнал вчера: Python class variable name vs __name__.

Вы можете обратиться к этому, используя type в качестве генератора классов в вашей заводской функции (как описано ниже в двух из приведенных ниже ответов). Однако это не решает проблему возможности импортировать класс, поскольку в то время как классы динамически построены в заводской функции, переменная, которую назначает вывод этой функции, является статической. Даже если он был динамическим, например ключ словаря, он должен находиться в пространстве имен модулей, чтобы быть импортированным из другого модуля. См. Мой ответ для более подробной информации.

ответ

1

[Примечание, это оригинальный плакат]

Таким образом, после некоторого мышления и говорить с людьми, я решил, что способность динамически создавать и присваивать переменные объектам класса в глобальном пространстве имен таким образом, это просто не то, что поддерживает Python (и, вероятно, с полным основанием). Хотя я считаю, что мой прецедент не слишком сумасшедший (выкачивание предопределенного списка идентично построенных классов), он просто не поддерживается.

Есть много вопросов, которые указывают на использование словаря в таком случае, как этот: https://stackoverflow.com/a/10963883/1216837. Я подумал о чем-то подобном, но проблема в том, что мне нужны эти классы в пространстве имен модулей, поэтому я могу импортировать их в другие модули. Однако, добавив их с globals(), например globals()['MyClass'] = class_dict['MyClass'], похоже, что он становится довольно там, и мое впечатление - люди, на которых нахмурились, используя globals() вот так.

Есть такие хаки, как предложенный patjenk, но в определенный момент обфускация и сложность излагают преимущества ясности декларирования каждого объекта класса статически. Поэтому, хотя это повторяется, я просто собираюсь выписать все определения классов. На самом деле, это в конечном итоге довольно краткий/ремонтопригодно:

Class1 = class_factory('class1') 
Class2 = class_factory('class2') 
... 
+0

Почему вам нужно изменить глобал если добавить сконструированных классы на внешнюю области действия контейнер (list_of_classes.append или Dict)? Сам контейнер будет импортироваться через модули, потому что контейнер будет находиться в области модуля. – cowbert

1

Это звучит как отрывочная идея. Но это здорово решить, вот как вы это делаете.

Насколько я понимаю, ваша проблема заключается в том, что вы хотите добавить динамически созданные классы в модуль. Я создал хак с помощью модуля и файл init .py.

dynamicModule/__ init__.py:

import dynamic 

class_names = ["One", "Two", "Three"] 

for new_name in class_names: 
    dynamic.__dict__['Class%s' % new_name] = type("Class%s" % (new_name), (object,), {'attribute_one': 'blah'}) 

dynamicModule/dynamic.py:

"""Empty file""" 

тест.ру:

import dynamicModule 
from dynamicModule import dynamic 
from dynamicModule.dynamic import ClassOne 

dynamic.ClassOne 
"""This all seems evil but it works for me on python 2.6.5""" 

__init__.py:

"""Empty file""" 
Смежные вопросы