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)
小智 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
所以在 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)
不确定这是否是最好的方法,但我是如何做到的。
对于 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另外,您可以以相同的方式添加更多查找,例如通过类名(不仅仅是像本示例中那样通过表名)
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)
| 归档时间: |
|
| 查看次数: |
10880 次 |
| 最近记录: |