Я создаю 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
в качестве генератора классов в вашей заводской функции (как описано ниже в двух из приведенных ниже ответов). Однако это не решает проблему возможности импортировать класс, поскольку в то время как классы динамически построены в заводской функции, переменная, которую назначает вывод этой функции, является статической. Даже если он был динамическим, например ключ словаря, он должен находиться в пространстве имен модулей, чтобы быть импортированным из другого модуля. См. Мой ответ для более подробной информации.
Почему вам нужно изменить глобал если добавить сконструированных классы на внешнюю области действия контейнер (list_of_classes.append или Dict)? Сам контейнер будет импортироваться через модули, потому что контейнер будет находиться в области модуля. – cowbert