Подтвердить что ты не робот

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 делает некоторые очень важные и совершенно отличные вещи, которые я не хочу ломать, но я полагаю, что для меня должен быть способ добавить немного кода конструктора в мета-класс моих моделей. Или я не понимаю.

4b9b3361

Ответ 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