How to use two different metadata properly to connect to two databases? #1539
-
First Check
Commit to Help
Example Codefrom typing import Optional
from sqlmodel import Field, SQLModel
import sqlalchemy
metadata1 = sqlalchemy.MetaData()
metadata2 = sqlalchemy.MetaData()
# all_metadata = sqlmodel.SQLModel.metadata
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
__table_args__ = (
metadata1, # This setting has no effect !! :(
)
class Boss(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
age: Optional[int] = None
__table_args__ = (
metadata2, # This setting has no effect !! :(
)
engine1 = sqlalchemy.create_engine("database 1")
engine2 = sqlalchemy.create_engine("database 2")
metadata1.create_all(engine1)
metadata2.create_all(engine2)
## in alembic's env.py
# target_metadata = {
# 'engine1': mymodel.metadata1,
# 'engine2': mymodel.metadata2
#} Description
Operating SystemLinux Operating System DetailsNo response SQLModel Version0.0.6 Python Version3.10.0 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 12 comments
-
This seems to work, although I did not test is thoroughly:
|
Beta Was this translation helpful? Give feedback.
-
Thank you @byrman I saw the definition of the SQLModel class metadata is a class variable Is the only way to handle multiple metadata is to extend the SQLModel class?
|
Beta Was this translation helpful? Give feedback.
-
In my experience there are often more solutions to a problem. I don't even know if extending the class is the right way to do it: I only tested table creation and select. Let us know how it works out for you. |
Beta Was this translation helpful? Give feedback.
-
By the way, |
Beta Was this translation helpful? Give feedback.
-
For now, I use it with alembic. env.py from myapp import mymodel
target_metadata = {
'engine1': mymodel.metadata1,
'engine2': mymodel.metadata2
} Probably, There is no problem to use it with alembic. But, I'm a little hesitant to use sqlmodel in production.. |
Beta Was this translation helpful? Give feedback.
-
I'm facing the same problem. But additionally I would like to work with transactions.
Which allows to use an unique Session to process transactions.
|
Beta Was this translation helpful? Give feedback.
-
Did anyone else ever figure this out? I'm looking to do something similiar (2 different fastapi endpoints using sqlmodel each tied to a different database) but am struggling to find any documentation that would support doing this. |
Beta Was this translation helpful? Give feedback.
-
I finally found a simple way to create tables in different databases. import uuid as uuid_pkg
from typing import Optional
from sqlalchemy.orm import registry
from sqlmodel import Field, SQLModel, create_engine
class DB1Base(SQLModel, registry=registry()):
pass
class DB2Base(SQLModel, registry=registry()):
pass
class DB1Table(DB1Base, table=True):
uuid: Optional[uuid_pkg.UUID] = Field(default_factory=uuid_pkg.uuid4, primary_key=True)
name: str = Field(nullable=False, index=True, unique=True)
class DB2Table(DB2Base, table=True):
uuid: Optional[uuid_pkg.UUID] = Field(default_factory=uuid_pkg.uuid4, primary_key=True)
name: str = Field(nullable=False, index=True, unique=True)
db1_engine = create_engine("postgresql://postgres@localhost:5432/db1")
db2_engine = create_engine("postgresql://postgres@localhost:5432/db2")
DB1Base.metadata.create_all(db1_engine)
DB2Base.metadata.create_all(db2_engine) |
Beta Was this translation helpful? Give feedback.
-
What a timing @vn7n24fzkq ! I spent the last few days on this and just came to the same solution ! Here is the explanation : SQLModel uses by default a single registry mapper, which means all classes names are gathered in a single namespace, causing name collisions if several databases contain classes with the same name. "sqlalchemy.exc.InvalidRequestError: Multiple classes found for path "xxx" in the registry of this declarative base. Please use a fully module-qualified path." Specifying a new instance of registry when creating a SQLModel subclass seems the right way to solve this issue. |
Beta Was this translation helpful? Give feedback.
-
This works! If like me you were struggling to combine this with using inheritance to extend models the solution is to use mixins to patch in columns as in #70 |
Beta Was this translation helpful? Give feedback.
-
I want a description of the recommended method in the official document. |
Beta Was this translation helpful? Give feedback.
-
WRONG!! x10^256 examples of also wrong. Don't mess up the registry On the right track but forgot something critical. Applying ^^^ quoted that so can copy+paste it and get reading ^^^ sqlalchemy docs screams to do this alembic docs has a whole section dedicated to this topic So what about SQLModel docs or issues? .... (crickets) Every example ever lacks applying naming convention to the registry metadata!
Why not to -- job security!Can only imagine that folks wanting to give every single database object a name manually would do that as a form of job security. If that is the case then carry on. For those of us who don't want to micro manage the micro manage of micro managers, would highly suggest not manually explicitly naming each and every database object. In each and every database. Not ever, but almost nearly ever. For example, what if the guy in charge of random database object names gets a girlfriend? It could happen and what a disaster that would be. For him, for her, and for all the people who have to try to track down why a foreign key or unique constraint doesn't exist when it's clear as day in the code that it should be there. Might have exaggerated, there is no agency of random database object names. But that won't stop the guy in charge of random database object names. Numerically, he can snap his fingers and poof, that agency will exist. Don't underestimate the power and unflinching resolve of the guy in charge of random database object names schemaschema is A bit of trivia, for sqlite, the default schema is "main". For postgresql, it's "public". The take awayEvery single SQLModel Base ever neglects, when creating a registry, to apply database objects naming convention. This includes the SQLModel source code. In Issues, when writing SQLModel Base(s), have mercy on those who are less knowledgeable by applying the registry correctly. |
Beta Was this translation helpful? Give feedback.
I finally found a simple way to create tables in different databases.