将 SQLAlchemy 声明式基础与 SQL 模型一起使用
Posted
技术标签:
【中文标题】将 SQLAlchemy 声明式基础与 SQL 模型一起使用【英文标题】:Using SQLAlchemy declarative base with SQL Model 【发布时间】:2022-01-05 08:31:17 【问题描述】:在我们的一个项目中,我们多年来(而且有很多)对所有模型都使用了 SQL Alchemy 声明性基础。 我们想尝试使用新的SQLModel library 来获取我们最新的模型声明。
为此,我们尝试将它与 Base 对象分开声明,并为两者调用 create_all
方法。
即:Base.metadata.create_all()
和 SQLModel.metadata.create_all()
。
但是用 SQLModel 声明的模型不能识别用 Base 声明的表。
目前,我们无法将之前的所有模型声明从 Base 更改为 SQLModel。
这是一个可重现的代码:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column
from sqlalchemy import Integer
from typing import Optional
from sqlmodel import Field, SQLModel
from sqlalchemy import String
# Declarative base object
Base = declarative_base()
class DummySATable(Base):
__tablename__ = 'dummy_table'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(32))
class DummyModelTable(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
dummy_table_id : int = Field(default=None, foreign_key='dummy_table.id')
name: str
Base.metadata.create_all(engine)
SQLModel.metadata.create_all(engine)
这是回溯:
NoReferencedTableError Traceback (most recent call last)
/tmp/ipykernel_307893/3665898561.py in <module>
24
25 Base.metadata.create_all(engine)
---> 26 SQLModel.metadata.create_all(engine)
27
~/project/venv/lib/python3.9/site-packages/sqlalchemy/sql/schema.py in create_all(self, bind, tables, checkfirst)
4783 if bind is None:
4784 bind = _bind_or_error(self)
-> 4785 bind._run_ddl_visitor(
4786 ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
4787 )
~/project/venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in _run_ddl_visitor(self, visitorcallable, element, **kwargs)
3108 def _run_ddl_visitor(self, visitorcallable, element, **kwargs):
3109 with self.begin() as conn:
-> 3110 conn._run_ddl_visitor(visitorcallable, element, **kwargs)
3111
3112 @util.deprecated_20(
~/project/venv/lib/python3.9/site-packages/sqlalchemy/engine/base.py in _run_ddl_visitor(self, visitorcallable, element, **kwargs)
2111
2112 """
-> 2113 visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
2114
2115 @util.deprecated(
~/project/venv/lib/python3.9/site-packages/sqlalchemy/sql/visitors.py in traverse_single(self, obj, **kw)
522 meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
523 if meth:
--> 524 return meth(obj, **kw)
525
526 def iterate(self, obj):
~/project/venv/lib/python3.9/site-packages/sqlalchemy/sql/ddl.py in visit_metadata(self, metadata)
820 tables = list(metadata.tables.values())
821
--> 822 collection = sort_tables_and_constraints(
823 [t for t in tables if self._can_create_table(t)]
824 )
~/project/venv/lib/python3.9/site-packages/sqlalchemy/sql/ddl.py in sort_tables_and_constraints(tables, filter_fn, extra_dependencies, _warn_for_cycles)
1284 continue
1285
-> 1286 dependent_on = fkc.referred_table
1287 if dependent_on is not table:
1288 mutable_dependencies.add((dependent_on, table))
~/project/venv/lib/python3.9/site-packages/sqlalchemy/sql/schema.py in referred_table(self)
3703
3704 """
-> 3705 return self.elements[0].column.table
3706
3707 def _validate_dest_table(self, table):
~/project/venv/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py in __get__(self, obj, cls)
1111 if obj is None:
1112 return self
-> 1113 obj.__dict__[self.__name__] = result = self.fget(obj)
1114 return result
1115
~/project/venv/lib/python3.9/site-packages/sqlalchemy/sql/schema.py in column(self)
2408
2409 if tablekey not in parenttable.metadata:
-> 2410 raise exc.NoReferencedTableError(
2411 "Foreign key associated with column '%s' could not find "
2412 "table '%s' with which to generate a "
NoReferencedTableError: Foreign key associated with column 'dummymodeltable.dummy_table_id' could not find table 'dummy_table' with which to generate a foreign key to target column 'id'
我错过了什么?有没有可能(有什么解决方法吗?)
【问题讨论】:
【参考方案1】:根据this thread,我终于找到了一种简单的方法。由于 SQLModel 继承了 SQLAlchemy 的 Metadata 对象,我们可以简单地将 SQLModel 的元数据对象绑定到 SQLAlchemy 的元数据对象:
# Declarative base object
Base = declarative_base()
SQLModel.metadata = Base.metadata
# Table declaration....
SQLModel.metadata.create_all(engine)
【讨论】:
以上是关于将 SQLAlchemy 声明式基础与 SQL 模型一起使用的主要内容,如果未能解决你的问题,请参考以下文章
在 SQLAlchemy 中处理插入时重复的主键(声明式样式)