37

テーブルの名前を指定して、そのテーブル名のモデルを返す関数を作成したいと思います。例えば:

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

def getModelFromTableName(tablename):
   ...something magical

したがって、getModelFromTableName('table')はModelクラスを返す必要があります。

FormAlchemyはpython3.2で動作せず、外部キーを適切に処理したいので、私の目的は、作成している単純なフォームジェネレーターで関数を使用することです。

getModelFromTableNameを機能させる方法について誰かが私にポインタを教えてもらえますか?

これが私が持っている1つのアイデアです(それは完全に間違っているかもしれません、私は以前にメタクラスを扱ったことがありません...)

ModelクラスをBaseおよび他のクラス(TableReg)から継承し、TableRegのクラスメタにModelを格納させるとどうなりますか。グローバルディクショナリまたはシングルトンのテーブル名。

Baseのメタクラスは、私が壊したくない非常に重要で完全に気の利いたことを行うので、これは完全にオフになる可能性があることを認識していますが、メタクラスにコンストラクターコードを少し追加する方法が必要だと思います私のモデルの。または私は理解していません。

4

9 に答える 9

53

イーブイのコメントに触発されました:

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
于 2014-05-20T08:19:26.197 に答える
12

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

fullnameTable属性です。以下を参照してください。

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

于 2016-02-04T11:11:41.293 に答える
12

そのため、SQLAlchemyバージョン1.4.x(2020-03-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

これが最善の方法かどうかはわかりませんが、私がどのように行ったかはわかりません。

于 2021-03-17T03:57:46.557 に答える
5

このためのユーティリティ関数がSQLAlchemy-Utilsに追加されました。詳細については、 get_class_by_tableのドキュメントを参照してください。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
于 2015-03-03T12:11:05.207 に答える
2

sqlalchemy 1.4.x(および将来の読者もおそらく2.0.x)の場合モデルが多くのファイルに分散している場合に便利になるようにErotemic回答をうまく拡張できます(このような場合は、適切に実行するときにORMクラスを検索する主な理由ですOOP)。

そのようなクラスを取り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)

次に、モデルを個別のモジュールで宣言することにより、 :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

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

そしてそれは期待通りに動作します:

>>> 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'>

functools.lru_cacheパフォーマンスを向上させるために、このメソッドの出力を安全にキャッシュできます(for不要な場合はPythonループを回避します)。また、同じ方法でルックアップを追加することもできます。たとえば、クラス名(この例のようなテーブル名だけでなく)を使用します。

于 2021-08-20T12:35:39.863 に答える
1

sqlalchemy automapを使用している場合は、わずかな変更が必要です。それを調整するために私に数分かかりました:

def get_class_by_tablename(tablename, Base):
  for c in Base._decl_class_registry.values():
    if c.__table__.name == tablename:
      return c
于 2018-09-27T15:04:20.153 に答える
1

__table_cls__1つの方法は、クラスメソッドをミックスイン内Baseまたはミックスインとして定義することです。私はこれをいかなる種類の継承でもテストしていないことに注意してください。拡張する場合の例Base

# Python 3.7.2, SQLAlchemy 1.3.13
###
# app/models.py
from sqlalchemy import Table
from sqlalchemy.ext.declarative import declarative_base

class Base(object):
    @classmethod
    def __table_cls__(cls, *args, **kwargs):
        t = Table(*args, **kwargs)
        t.decl_class = cls
        return t

Base = declarative_base(cls=Base)

テーブルの例を考案する:

# app/models.py
class Account(Base):  # Note this is the customized Base class
    __tablename__ = 'account'
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    account_number = Column(String)

コンソールでのテスト:

# Python 3.7.2 Console
>>> from app import models as m
>>> account_table = m.Base.metadata.tables['account']
>>> account_table.__class__
<class 'sqlalchemy.sql.schema.Table'>

>>> account_table.decl_class
<class 'app.models.Account'>

関数として:

def get_class_by_table_name(table_name):
    tbl = Base.metadata.tables[table_name]
    return tbl.decl_class

補足:多くの回答が参照しているBase._decl_class_registryため、この関数は名前付きパラメーターdeclarative_base()の辞書も受け入れます。class_registry

>>> registered_classes = {}
>>> Base = declarative_base(cls=Base, class_registry=registered_classes)
>>> registered_classes.keys()
dict_keys(['Account', '_sa_module_registry', 'AccountType', ...])
于 2020-03-04T20:28:46.920 に答える
0

https://stackoverflow.com/a/23754464/12903047

フラスコの場合-sqlachemyはそれを使用します:

db.Model.registry._class_registry.values()

于 2021-04-21T10:19:49.330 に答える
-1

これを削除するつもりでしたが、コメントでの議論は、いくつかの良い習慣について知りたい人にとって役立つかもしれないと思います。塩のピンチでこの答えを取ります。


このようなものがトリックを行います:

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
于 2012-07-30T09:03:12.267 に答える