SQLAlchemy:从表名中获取Model.就我所见,这可能意味着将一些函数附加到元类构造函数

She*_*ena 18 sqlalchemy foreign-keys python-3.x

我想创建一个函数,给定表的名称,返回具有该名的模型.例如:

class Model(Base):
    __tablename__ = 'table'
    ...a bunch of Columns

def getModelFromTableName(tablename):
   ...something magical
Run Code Online (Sandbox Code Playgroud)

所以getModelFromTableName('table')应该返回Model类.

我的目标是在我正在制作的简单表单生成器中使用该函数,因为FormAlchemy不能与python3.2一起工作,我希望它能很好地处理外键.

任何人都可以给我任何关于如何让getModelFromTableName工作的指针吗?

这是我的一个想法(可能完全错误,我之前没有使用过meta类)

如果我要使我的Model类继承自Base以及其他一些类(TableReg)并具有TableReg商店Model的类meta,该怎么办?tablename在某些全局字典或Singleton中.

我意识到这可能是完全关闭的,因为Base的元类做了一些非常重要且非常好的东西,我不想打破,但我认为必须有一种方法让我在元类中附加一些构造函数代码我的模特 或者我不明白.

Ora*_*Tux 35

灵感来自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
Run Code Online (Sandbox Code Playgroud)

  • 效果很好 - 对于flask-sqlalchemy,用db.Model替换Base,因为它们是相同的(或多或少). (4认同)

小智 8

注意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
Run Code Online (Sandbox Code Playgroud)

fullname 是一个Table属性,请参阅:

github.com/sqlalchemy/sqlalchemy/blob/master/lib/sqlalchemy/sql/schema.py#L530-L532


Ero*_*mic 8

所以在 SQLAlchemy 1.4.x 版(我更新到 2020 年 3 月 16 日左右)中,它似乎_decl_class_registry不再存在。

我能够使用新的registry类属性(不受保护,所以希望它不会突然被删除!)。

Base.TBLNAME_TO_CLASS = {}

for mapper in Base.registry.mappers:
    cls = mapper.class_
    classname = cls.__name__
    if not classname.startswith('_'):
        tblname = cls.__tablename__
        Base.TBLNAME_TO_CLASS[tblname] = cls
Run Code Online (Sandbox Code Playgroud)

不确定这是否是最好的方法,但我是如何做到的。


igo*_*993 6

对于 sqlalchemy 1.4.x(也可能是未来读者的 2.0.x),当模型分布在许多文件中时,您可以很好地扩展Erotemic答案,使其更加方便(这种情况是在执行正确操作时查找 ORM 类的主要原因)面向对象编程)。

参加这样的课程并Base从中制作一个:

from sqlalchemy.orm import declarative_base

class BaseModel:

    @classmethod
    def model_lookup_by_table_name(cls, table_name):
        registry_instance = getattr(cls, "registry")
        for mapper_ in registry_instance.mappers:
            model = mapper_.class_
            model_class_name = model.__tablename__
            if model_class_name == table_name:
                return model


Base = declarative_base(cls=BaseModel)
Run Code Online (Sandbox Code Playgroud)

然后声明您的模型,即使在单独的模块中,也使您能够使用cls.model_lookup_by_table_name(...)方法而无需导入任何内容,只要您派生自Base

user_models.py

from sqlalchemy import Column, Integer

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)

    # ... and other columns

    def some_method(self):
        # successfully use lookup like this
        balance_model = self.model_lookup_by_table_name("balance")
        # ...
        return balance_model
Run Code Online (Sandbox Code Playgroud)

balance_models.py

from sqlalchemy import Column, Integer

class Balance(Base):
    __tablename__ = "balance"

    id = Column(Integer, primary_key=True)

    # ... other columns

    def some_method(self):
        # lookup works on every model
        user_model = self.model_lookup_by_table_name("user")
        # ...
        return user_model
Run Code Online (Sandbox Code Playgroud)

它按预期工作:

>>> User().some_method()
<class 'balance_models.Balance'>
>>> Balance().some_method()
<class 'user_models.User'>
>>> Base.model_lookup_by_table_name("user")
<class 'user_models.User'>
>>> Base.model_lookup_by_table_name("balance")
<class 'balance_models.Balance'>
Run Code Online (Sandbox Code Playgroud)

您可以安全地缓存此方法的输出以提高性能(在不需要时functools.lru_cache避免 python循环)。for另外,您可以以相同的方式添加更多查找,例如通过类名(不仅仅是像本示例中那样通过表名)


Kon*_*nen 5

SQLAchemy-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
Run Code Online (Sandbox Code Playgroud)

  • 该函数采用表对象(完全相同,`c.__table__ is table`),而不是表名。 (2认同)