0

I have the following issue in a project that uses SQLAlchemy. I've put all the code after the question.

If I use from base import Base in create_tables.py (as seen in the code below), the tables are not created. If I use from persistence.base import Base in create_tables.py, the tables ARE created.

Why does this happen? Why does using the absolute import path fix everything?

I suspect that when I use from base import Base a new, clean Base class instance is used, so SQLAlchemy does not know of the child classes that inherit from Base and make up the tables. However, I don't understand why using the absolute import path (persistence.Base) actually solves the issue.

Another point to make is that I use absolute import paths in the *_api.py files as you can see below. How would using relative path imports and absolute path imports affect the outcome of this situation?

Any ideas? Thanks.

Project's structure:

src
--persistence
----create_tables.py
----base.py
----user
------user_api.py
----expenditure
------expenditure.py
----statement
------statement.py

Relevant code:

base.py

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

expenditure_api.py

from sqlalchemy import Column, Float, ForeignKey, String, TIMESTAMP
from sqlalchemy.dialects.mysql import INTEGER
from sqlalchemy.orm import relationship

from persistence.base import Base

class Expenditure(Base):
    __tablename__ = 'expenditure'

    expenditure_id = Column(INTEGER, primary_key=True, unique=True)
    expenditure_user_id = Column(ForeignKey('user.user_id'), nullable=False, index=True)
    expenditure_issue_date = Column(TIMESTAMP)
    expenditure_payment_date = Column(TIMESTAMP)
    expenditure_receipt_id = Column(String(45))
    expenditure_statement_id = Column(ForeignKey('statement.statement_id'), index=True)
    expenditure_transaction_details = Column(String(255))
    expenditure_payment_num = Column(INTEGER)
    expenditure_total_payments = Column(INTEGER)
    expenditure_amount = Column(Float(asdecimal=True), nullable=False)
    expenditure_currency = Column(String(45), nullable=False)

    expenditure_statement = relationship('Statement')
    expenditure_user = relationship('User')

statement_api.py

# coding=utf-8

from sqlalchemy import Column, Float, ForeignKey, String, TIMESTAMP
from sqlalchemy.dialects.mysql import INTEGER

from persistence.base import Base

class Statement(Base):
    __tablename__ = 'statement'

    statement_id = Column(String(255), primary_key=True, unique=True)
    statement_from_date = Column(TIMESTAMP)
    statement_to_date = Column(TIMESTAMP)

user_api.py

from sqlalchemy import Column, Float, ForeignKey, String, TIMESTAMP
from sqlalchemy.dialects.mysql import INTEGER

from persistence.base import Base

class User(Base):
    __tablename__ = 'user'

    user_id = Column(INTEGER, primary_key=True, unique=True)
    user_username = Column(String(45), nullable=False)
    user_full_name = Column(String(255))

create_tables.py

from sqlalchemy import exc
from sqlalchemy.orm import sessionmaker

from persistence_config import sql_server_socket, database_name

from user.user_api import User
from expenditure.expenditure_api import Expenditure
from statement.statement_api import Statement

from base import Base

from create_logger import create_logger

logger = create_logger(__name__)

def create_tables(engine):

    # try:
        Base.metadata.create_all(engine)
        logger.info(f"Tables created.")
    # except exc.DatabaseError as ex:
    #     logger.error(f"create_tables error: {ex}")
    # except:
    #     logger.exception("create_tables error.")


if __name__ == "__main__":
    from persistence_config import sql_server_socket, database_name
    from sqlalchemy import create_engine
    
    engine = create_engine(sql_server_socket + "/" + database_name, echo=True)
    create_tables(engine)
user3290570
  • 5
  • 1
  • 4
  • 1
    How do you run `create_tables.py` and from which directory? My memory is a bit hazy on the subject, but I don't think `from base import ...` is relative in Python3, but absolute, and so your guess would be right on the money; you end up with 2 different `Base` classes from different module instances. https://stackoverflow.com/questions/16981921/relative-imports-in-python-3 is a good read. – Ilja Everilä Mar 19 '21 at 19:44
  • I am running it as a script. As per my environment settings, sys.path is like this: ['D:\\projectRootDir\\\\src\\persistence', 'D:\\projectRootDir\\\\src', 'D:\\projectRootDir\\', ...] – user3290570 Mar 19 '21 at 22:36
  • Aye, your Python path is off, and so you are able to (absolute) import the same code twice as 2 separate modules. Read the linked Q/A, it covers your situation and has the solution to it (running scripts from within packages). – Ilja Everilä Mar 21 '21 at 10:40

0 Answers0