Skip to content

How to Models With Same Class Name in SQLModel

The other day I was working on a project where I had two models with the same class name called Workflow reside in different packages, and when I started the tests I was given the following error:

deal with sqlalchemy.exc.InvalidRequestError: Multiple classes found for path "Workflow" in the registry of this declarative base. Please use a fully module-qualified path.

After quite some time of investigation I found a solution:

## my_package1/models.py

from sqlmodel import (
    SQLModel as _SQLModel,
    MetaData,
)
from sqlalchemy.orm import registry

class SQLModel(_SQLModel, registry=registry()):
    metadata = MetaData()

class Workflow(SQLModel, table=True):
    __tablename__ = "workflow"

## my_package2/models.py

from sqlmodel import (
    SQLModel as _SQLModel,
    MetaData,
)
from sqlalchemy.orm import registry

class SQLModel(_SQLModel, registry=registry()):
    metadata = MetaData()

class Workflow(SQLModel, table=True):
    # this is needed unless 2 workflow tables are created in two different databases
    __tablename__ = "internal_workflow"

In the example above we have two classes with the same name Workflow in different packages. The solution is to create a base class SQLModel that has a registry and metadata, and then inherit from it in each package.

On your application startup, you can create the tables for each package by importing the SQLModel class from each package and calling the metadata.create_all method as follows:

from my_package1.models import SQLModel as MyPackage1SQLModel
from my_package2.models import SQLModel as MyPackage2SQLModel
from sqlmodel import create_engine
engine = create_engine("sqlite:///:memory:")

def on_startup():
    MyPackage1SQLModel.metadata.create_all(engine)
    MyPackage2SQLModel.metadata.create_all(engine)

By doing this, two models won't interfere with each other.

Note that in the example above two workflow models need to have different table names as they uses the same database.

That being said if you have two different databases, you can omit the __tablename__ attribute in the Workflow class. It can be achieved via initiating two seperate engines and creating the tables in each engine.

engine1 = create_engine("sqlite:///:memory:")
engine2 = create_engine("sqlite:///:memory:")

def on_startup():
    MyPackage1SQLModel.metadata.create_all(engine1)
    MyPackage2SQLModel.metadata.create_all(engine2)

Conclusion

By creating a base class SQLModel that has dedicated registry and metadata, you can have multiple models with the same class name in different packages.

It is a very simplistic solution to a very nuanced problem, but it works for me for a very specific use case.