I was trying to following the design pattern shown in this previous question related to SQLAlchemy and intended to share a common Base instance across multiple files. The code exactly as is works on python2 and python3.
However, when I move the files a.py, b.py, c.py, and base.py in a module (called model) and add the necessary init.py file, it continues to work on python2 but then produces an error on python3 (details below).
I have the following files:
from sqlalchemy.ext.declarative import declarative_base Base = declarative_base()
from sqlalchemy import * from base import Base from sqlalchemy.orm import relationship class A(Base): __tablename__ = "A" id = Column(Integer, primary_key=True) Bs = relationship("B", backref="A.id") Cs = relationship("C", backref="A.id")
from sqlalchemy import * from base import Base class B(Base): __tablename__ = "B" id = Column(Integer, primary_key=True) A_id = Column(Integer, ForeignKey("A.id"))
from sqlalchemy import * from base import Base class C(Base): __tablename__ = "C" id = Column(Integer, primary_key=True) A_id = Column(Integer, ForeignKey("A.id"))
from sqlalchemy import create_engine from sqlalchemy.orm import relationship, backref, sessionmaker from model import base from model import a from model import b from model import c engine = create_engine("sqlite:///:memory:") base.Base.metadata.create_all(engine, checkfirst=True) Session = sessionmaker(bind=engine) session = Session() a1 = a.A() b1 = b.B() b2 = b.B() c1 = c.C() c2 = c.C() a1.Bs.append(b1) a1.Bs.append(b2) a1.Cs.append(c1) a1.Cs.append(c2) session.add(a1) session.commit()
$ python main.py ; echo $? 0
python3 errs with:
$ python3 main.py ; echo $? Traceback (most recent call last): File "main.py", line 7, in <module> from model import a File "/home/shale/code/py/try/model/a.py", line 2, in <module> from base import Base ImportError: No module named base 1
I ultimately solved this by putting the code from base.py into my init.py file (described as one answer below), but does anyone know why this produces an error in python3 but not in python2? What change is responsible for this in the first place?
Python 3 switches to absolute imports by default, and disallows unqualified relative imports. The
from base import Base line is such an import.
Python 3 will only look for top-level modules; you don’t have a
base top-level module, only
model.base. Use a full module path, or use relative qualifiers:
from .base import Base
. at the start tells Python 3 to import starting from the current package.
You can enable the same behaviour in Python 2 by adding:
from __future__ import absolute_import
This is a change introduced by PEP 328, and the
from future import is available from Python 2.5 onwards.