How do I split an SQLAlchemy Declarative model into modules?
How do I split an SQLAlchemy Declarative model into modules?
I need to define multiple modules that contain SQLAlchemy Declarative classes. I've written a function in each module called subclass_base()
into which a Base instance of declarative_base()
is passed after instantiation. The first module's subclass_base()
call correctly subclasses the Base instance and the subclasses are visible from outside the function. The second module's call finishes without errors but from both within the function and outside of it all of the subclasses are reflected in Base.__subclasses__
only some of the time. Here is a minimal working example with only 1 class definition in each module:
subclass_base()
declarative_base()
subclass_base()
Base.__subclasses__
modela.py
from sqlalchemy import Column, Integer, String
def subclass_base(Base):
class Roles(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True)
name = Column(String(32))
modelb.py
from sqlalchemy import Column, Integer, String
def subclass_base(Base):
class Locations(Base):
__tablename__ = 'locations'
id = Column(Integer, primary_key=True)
name = Column(String(64))
test.py
from sqlalchemy.ext.declarative import declarative_base
from modela import subclass_base as amod
from modelb import subclass_base as bmod
def test():
count = 0
while True:
try:
Base = declarative_base()
amod(Base)
bmod(Base)
sc = [subclass.__name__ for subclass in Base.__subclasses__()]
assert(len(sc) == 2)
print('.', end='')
count += 1
except AssertionError:
print("Failed after {} successful pass(es)".format(count))
count = 0
I suspect this issue is me overlooking a specific issue of the metaclass work that goes on with declarative_base() but I can't seem to figure out what's happening. I'm also wondering if this is an inheritance issue. Is there a different architectural approach that I should take rather than using a function to subclass a single Base class?
1 Answer
1
Don't define the class inside a function. Just define the Base
in a single definition module then import that module from the other modules:
Base
db.py
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
Session = sessionmaker()
def bind_engine(engine):
Base.metadata.bind = engine
Session.configure(bind=engine)
modela.py
from sqlalchemy import Column, Integer, String
from db import Base
class Roles(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True)
name = Column(String(32))
modelb.py
from sqlalchemy import Column, Integer, String
from db import Base
class Locations(Base):
__tablename__ = 'locations'
id = Column(Integer, primary_key=True)
name = Column(String(64))
user_script.py
from sqlalchemy import create_engine
engine = create_engine('postgresql://user:pwd@server/database')
from yourproject.db import bind_engine
bind_engine(engine)
Why are you using the user's
Base
? You shouldn't. If the user wants, they can create their own Base
! @DeanStamler– nosklo
Jun 29 at 17:24
Base
Base
So that's interesting. Multiple Bases can share a single database? Do they share a session or engine too? Perhaps that's the solution.
– Dean Stamler
Jun 29 at 17:25
@DeanStamler the user can provide their engine for you to bind on your base. I edited the answer to show this:
– nosklo
Jun 29 at 17:29
@DeanStamler about sharing sessions, well, it depends on the use case, if your user wants, they can use your sessionmaker, or they can make their own sessions for their purpose
– nosklo
Jun 29 at 17:30
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
This doesn't solve my particular problem: one of the modules is intended to be packaged for distribution via pip and so the end user cannot be required to edit the module once installed. Since the end user instantiates their own Base, I need to somehow pass it to the module. Even if I forced the user to use the module's Base, that would still prevent a second module from using the base. The goal is to modularize the app into useful components that have dedicated tables in a shared database.
– Dean Stamler
Jun 29 at 17:22