g_t_m
g_t_m

Reputation: 714

Is it possible to use SQLAlchemy where the models are defined in different files?

I'm using SQLAlchemy for the first time and trying to define my models / schema. My only experience with ORM prior to this was with Rails and ActiveRecord.

Anyway, following SQLAlchemy's ORM Quick Start, this is the basic example they use (I have removed a few lines which are not relevant to my question):

from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "user_account"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))

    addresses: Mapped[list["Address"]] = relationship(
        back_populates="user", cascade="all, delete-orphan"
    )

class Address(Base):
    __tablename__ = "address"

    id: Mapped[int] = mapped_column(primary_key=True)
    email_address: Mapped[str]
    user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))

    user: Mapped["User"] = relationship(back_populates="addresses")

My question is: is it possible to create two models in separate files (user.py and address.py), defining each model in its own file, and then import them and run a command like the following in order to instantiate the database:

from sqlalchemy import create_engine
engine = create_engine("sqlite://", echo=True)
Base.metadata.create_all(engine)

Upvotes: 0

Views: 2425

Answers (2)

Pierre Massé
Pierre Massé

Reputation: 822

I would like to extend on Brian's answer. When using an approach similar to his, I ran into an ImportError: cannot import name 'ModelBase' from partially initialized module 'lib' (most likely due to a circular import).

I just made a really quick change that did remove this error, by simply switching the order of ModelBase declaration and models imports in __init__.py:

# models.__init__.py
from sqlalchemy.orm import DeclarativeBase

class ModelBase(DeclarativeBase):
    pass

from .user import User
from .address import Address

Upvotes: 2

Brian H.
Brian H.

Reputation: 2235

Yes -- what you are asking is both possible and is the standard way of creating SQLAlchemy Schemas.

The only gotcha is that you want all of your models to inherit from the same DeclarativeBase. This is essentially what registers them with SQLAlchemy and other plugins/tools like Alembic.

There are a few organization structures you can use, but a simple and common one is something like this:

- project_root
  - models
    + __init__.py
    + user.py
    + address.py
  - utils
  - scripts

In models.__init__, create and export your Base (I'd recommend giving it a slightly more specific name, such as ModelBase). Then in each of your model files, just import ModelBase and cut/paste your existing code.

Some projects enforce a single model per file. Others group related models together.

Since all of your models import and use a single DeclaritiveBase, you can call methods like BaseModel.metadata.create_all(). Just keep in mind you will still need to import those actual models somewhere.

An easy way to handle that is to import them inside of models.__init__.py. That way you will essentially load them whenever you import and use ModelBase.

# models.__init__.py
from .user import User
from .address import Address

class ModelBase(DeclarativeBase):
    pass

Upvotes: 2

Related Questions