最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Setting up Alembic for a Kivy android app - Stack Overflow

programmeradmin4浏览0评论

I am currently trying to build a mobile app using Python, Kivy and SQLAlchemy. However, I am having difficulty implementing database migrations using Alembic.

Basically, the migrations work just fine on my computer but Alembic is not finding any migrations when called from my phone. I am using buildozer for the APK building.

My guess is that Alembic is not finding the migrations files from the versions folder when run on android, probably because of a path issue.

Additional notes on the app functioning on Android :

  • The database is created correctly when the app the launched for the first time

  • When the app is launched, Alembic starts to run but doesn't find any migrations to apply. Then the app exits without any error code.

  • If I replace manually the database with one already up-to-date, the app runs smootly.

  • If I replace the database with one initialized but not up-to-date (containing the initial migration but not the updates). I get this error using adb logcat :

    • 02-17 21:27:48.451 17547 21720 I python  : Migration failed: Can't locate revision identified by '2c282135e834'
      02-17 21:27:48.933 17547 21720 I python  : Python for android ended.
      

For reference here is my current code. I left all the debugging prints I added, hopefully it can help...

The Kivy App definition :

class AStatApp(MDApp):
    def build(self):
        self.theme_cls.theme_style = "Light"
        self.theme_cls.primary_palette = "Darkred"

        db_path = get_db_path()
        
        # This is the function currently not working
        try:
            run_migrations()
        except Exception as e:
            print(f"Migration failed: {e}")

        self.Session = self.init_db(db_path)

        Builder.load_file("kv/selector.kv")
        Builder.load_file("kv/ascent-list-screen.kv")
        Builder.load_file("kv/ascent-screen.kv")
        Builder.load_file("kv/settings-screen.kv")
        Builder.load_file("kv/area-screen.kv")
        Builder.load_file("kv/statistic-screen.kv")
        Builder.load_file("kv/statistic-filter-screen.kv")
        Builder.load_file("kv/to-do-list-screen.kv")
        Builder.load_file("kv/screenmanager.kv")
        return MainScreenManager()

The get_db_path():

def get_db_path():
    db_filename = "astat.db"
    if platform == "win":
        data_dir = os.getcwd()
    elif platform == "android":
        # Get Android context
        from jnius import autoclass, cast

        PythonActivity = autoclass(".kivy.android.PythonActivity")
        context = cast("android.content.Context", PythonActivity.mActivity)

        # Get external storage path for the app
        file_p = cast("java.io.File", context.getExternalFilesDir(None))
        data_dir = file_p.getAbsolutePath()

    writable_db_path = os.path.join(data_dir, db_filename)

    print(f"Final DB path: {writable_db_path}")
    print(f"Path exists: {os.path.exists(writable_db_path)}")

    return writable_db_path

The run_migrations()

def run_migrations():
    """Run migrations using Alembic's command API with proper configuration."""
    db_path = get_db_path()
    
    # Creates database file if it doesnt exist
    if not os.path.exists(db_path):
        open(db_path, "a").close()

    base_dir = os.path.abspath(os.path.dirname(__file__))
    script_location = os.path.join(base_dir, "migrations")

    # Configure Alembic programmatically
    alembic_cfg = Config("alembic.ini")
    alembic_cfg.set_main_option("script_location", script_location)
    alembic_cfg.set_main_option("sqlalchemy.url", f"sqlite:///{db_path}")

    # Debug prints
    db_url = alembic_cfg.get_main_option("sqlalchemy.url")
    migrations = os.listdir(os.path.join(script_location, "versions"))
    print(f"database url : {db_url}")
    print(f"migration path: {script_location}")
    print(f"Migration versions: {migrations}")

    try:
        command.upgrade(alembic_cfg, "head")
        print("Database migrations applied successfully.")
    except SystemExit as e:
        if e.code != 0:
            print(f"Migration failed with code {e.code}")
            raise

the env.py:

config = context.config

if config.config_file_name is not None:
    fileConfig(config.config_file_name)

target_metadata = Base.metadata

def run_migrations_offline() -> None:
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """

    url = config.get_main_option("sqlalchemy.url")

    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online() -> None:
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    from alembic.script import ScriptDirectory

    script = ScriptDirectory.from_config(config)
    
    # Debug prints
    print(ScriptDirectory.versions)
    print("Discovered migrations:")
    for rev in script.walk_revisions():
        print(f"- {rev.revision} ({rev.doc})")
        
    db_path = get_db_path()
    print(f"Android DB Path: {db_path}")
    print(f"File exists: {os.path.exists(db_path)}")
    print(f"Migration files: {os.listdir(os.path.dirname(__file__))}")


    # Actual migration run
    connectable = create_engine(f"sqlite:///{db_path}")

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()

if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

So far, I have tried many things to reset the way Alembic is looking for migrations but nothing works (and I have a hard time remembering everything I tried...)

Also, with the current code, this is the adb logcat i get when I launch the app for the first time (I removed everything before that is just about the normal kivy app setup) :

02-17 21:19:43.422 12846 17556 I python  : [INFO   ] [Window      ] auto add sdl2 input provider
02-17 21:19:43.423 12846 17556 I python  : [INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
02-17 21:19:43.672 12846 17556 I python  : [INFO   ] [Clipboard   ] Provider: android
02-17 21:19:43.912 12846 17556 I python  : Final DB path: /data/user/0/com.cmareau.astat/files/astat.db
02-17 21:19:43.912 12846 17556 I python  : Path exists: False
02-17 21:19:43.913 12846 17556 I python  : Final DB path: /data/user/0/com.cmareau.astat/files/astat.db
02-17 21:19:43.913 12846 17556 I python  : Path exists: False
02-17 21:19:43.914 12846 17556 I python  : database url : sqlite:////data/user/0/com.cmareau.astat/files/astat.db
02-17 21:19:43.914 12846 17556 I python  : migration path: /data/data/com.cmareau.astat/files/app/migrations
02-17 21:19:43.914 12846 17556 I python  : Migration path content: ['2c282135e834_initial_migration.pyc', '3b4994652668_populate_grade_table.pyc', '__pycache__', 'db33e18bf97f_adding_of_todolist_sector_and_climbtodo_.pyc']
02-17 21:19:43.920 12846 17556 I python  : Final DB path: /data/user/0/com.cmareau.astat/files/astat.db
02-17 21:19:43.920 12846 17556 I python  : Path exists: True
02-17 21:19:43.920 12846 17556 I python  : Android DB Path: /data/user/0/com.cmareau.astat/files/astat.db
02-17 21:19:43.920 12846 17556 I python  : File exists: True
02-17 21:19:43.920 12846 17556 I python  : Migration files: ['README', '__pycache__', 'env.pyc', 'script.py.mako', 'versions']
02-17 21:19:43.936 12846 17556 I python  : Database migrations applied successfully.
02-17 21:19:44.232 12846 17556 I python  : Python for android ended.
发布评论

评论列表(0)

  1. 暂无评论