SQLAlchemy: получить модель из имени таблицы. Это может означать добавление некоторой функции в конструктор метакласса, насколько я могу видеть
Я хочу создать функцию, которая, учитывая имя таблицы, возвращает модель с этим tablename.
Например:
class Model(Base):
__tablename__ = 'table'
...a bunch of Columns
def getModelFromTableName(tablename):
...something magical
поэтому getModelFromTableName ('table') должно возвращать класс Model.
Моя цель - использовать функцию в генераторе простой формы, которую я создаю, поскольку FormAlchemy не работает с python3.2, и я хочу, чтобы он отлично обрабатывал внешние ключи.
Может ли кто-нибудь указать мне, как заставить getModelFromTableName работать?
Вот одна идея, которую я имею (это может быть совершенно неправильно, я не работал с метаклассами раньше...)
Что делать, если мои классы Model наследуют от Base, а также некоторых других классов (TableReg) и имеют класс meta TableReg store Model. tablename в некотором глобальном словаре или Singleton.
Я понимаю, что это может быть полностью отключено, потому что Base metaclass делает некоторые очень важные и совершенно отличные вещи, которые я не хочу ломать, но я полагаю, что для меня должен быть способ добавить немного кода конструктора в мета-класс моих моделей. Или я не понимаю.
Ответы
Ответ 1
Вдохновленный Eevee комментарий:
def get_class_by_tablename(tablename):
"""Return class reference mapped to table.
:param tablename: String with name of table.
:return: Class reference or None.
"""
for c in Base._decl_class_registry.values():
if hasattr(c, '__tablename__') and c.__tablename__ == tablename:
return c
Ответ 2
Утилита для этой функции была добавлена в SQLAlchemy-Utils. Подробнее см. get_class_by_table docs. Решение в SQLAlchemy-Utils также может охватывать сценарии наследования отдельных таблиц.
import sqlalchemy as sa
def get_class_by_table(base, table, data=None):
"""
Return declarative class associated with given table. If no class is found
this function returns `None`. If multiple classes were found (polymorphic
cases) additional `data` parameter can be given to hint which class
to return.
::
class User(Base):
__tablename__ = 'entity'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
get_class_by_table(Base, User.__table__) # User class
This function also supports models using single table inheritance.
Additional data paratemer should be provided in these case.
::
class Entity(Base):
__tablename__ = 'entity'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
type = sa.Column(sa.String)
__mapper_args__ = {
'polymorphic_on': type,
'polymorphic_identity': 'entity'
}
class User(Entity):
__mapper_args__ = {
'polymorphic_identity': 'user'
}
# Entity class
get_class_by_table(Base, Entity.__table__, {'type': 'entity'})
# User class
get_class_by_table(Base, Entity.__table__, {'type': 'user'})
:param base: Declarative model base
:param table: SQLAlchemy Table object
:param data: Data row to determine the class in polymorphic scenarios
:return: Declarative class or None.
"""
found_classes = set(
c for c in base._decl_class_registry.values()
if hasattr(c, '__table__') and c.__table__ is table
)
if len(found_classes) > 1:
if not data:
raise ValueError(
"Multiple declarative classes found for table '{0}'. "
"Please provide data parameter for this function to be able "
"to determine polymorphic scenarios.".format(
table.name
)
)
else:
for cls in found_classes:
mapper = sa.inspect(cls)
polymorphic_on = mapper.polymorphic_on.name
if polymorphic_on in data:
if data[polymorphic_on] == mapper.polymorphic_identity:
return cls
raise ValueError(
"Multiple declarative classes found for table '{0}'. Given "
"data row does not match any polymorphic identity of the "
"found classes.".format(
table.name
)
)
elif found_classes:
return found_classes.pop()
return None
Ответ 3
Остерегайтесь ответа OrangeTux, который не учитывает схемы. Если у вас есть омонимы в разных схемах, используйте:
def get_class_by_tablename(table_fullname):
"""Return class reference mapped to table.
:param table_fullname: String with fullname of table.
:return: Class reference or None.
"""
for c in Base._decl_class_registry.values():
if hasattr(c, '__table__') and c.__table__.fullname == table_fullname:
return c
fullname
- атрибут таблицы: https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/sql/schema.py#L455
Ответ 4
Я собирался удалить это, но считаю, что обсуждение комментариев может быть полезно для людей, которые хотят знать о некоторых хороших практиках. Возьмите этот ответ с щепоткой соли.
что-то вроде этого делает трюк:
def getModelFromTableName(sTable):
"""
return the Model class with the given __tablename__
"""
globals = globals()
for k in globals:
if type(globals[k]) == sqlalchemy.ext.declarative.DeclarativeMeta:
try:
if globals[k].__tablename__ == sTable:
return globals[k]
except:
pass
return None