pythonmysqlflasksqlalchemy

flask and sqlalchemy db upgrade creates mysql database tables on local host but creates mysql error 1146 on linux vps


I am at my wits end and realise the problem I have is not very defined. I am happy to provide anything else necessary for trouble shooting.

I am trying to deploy a web app that I have build. It is running fine on my local computer running a python 3.10 venv on pycharm and a local mysql database. I have cloned the git repository on the Linux VPS running ubuntu 22.04. I have installed all requirements and a mysql server with a user 'gnome' and a database 'gnome_hat'.

on sudo mysql -u root I can use gnome_hat, but SHOW TABLES; indicates it is empty (this is expected since I want them to be created when I first initialise my db.models through flask

my .env is set to DATABASE_URL=mysql+pymysql://gnome:@localhost:3306/gnome_hat I know this is working because I used mysql root on the local machine and that (obviously) didn't work and I was told I couldnt use that user, changing the .env worked.

However when I use flask db upgrade in the venv I am given a number of problems in the code described as sqlalchemy.exc.ProgrammingError: (pymysql.err.ProgrammingError) (1146, "Table 'gnome_hat.stakeholder' doesn't exist") and (Background on this error at: https://sqlalche.me/e/20/f405). However, I expect the database to be empty and the table stakeholders to not exist yet, since this initial flask db upgrade is supposed to create the tables.

(venv) root@ubuntu:~/gnome_hat# flask db upgrade
Traceback (most recent call last):
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1965, in _exec_single_context
    self.dialect.do_execute(
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 921, in do_execute
    cursor.execute(statement, parameters)
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/cursors.py", line 153, in execute
    result = self._query(query)
             ^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/cursors.py", line 322, in _query
    conn.query(q)
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 558, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 822, in _read_query_result
    result.read()
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 1200, in read
    first_packet = self.connection._read_packet()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 772, in _read_packet
    packet.raise_for_error()
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/protocol.py", line 221, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/err.py", line 143, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.ProgrammingError: (1146, "Table 'gnome_hat.stakeholder' doesn't exist")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/root/gnome_hat/venv/bin/flask", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/flask/cli.py", line 1063, in main
    cli.main()
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/click/core.py", line 1682, in invoke
    cmd_name, cmd, args = self.resolve_command(ctx, args)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/click/core.py", line 1729, in resolve_command
    cmd = self.get_command(ctx, cmd_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/flask/cli.py", line 578, in get_command
    app = info.load_app()
          ^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/flask/cli.py", line 312, in load_app
    app = locate_app(import_name, None, raise_if_not_found=False)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/flask/cli.py", line 218, in locate_app
    __import__(module_name)
  File "/root/gnome_hat/app/__init__.py", line 23, in <module>
    from app import routes, models, errors
  File "/root/gnome_hat/app/routes.py", line 4, in <module>
    import main
  File "/root/gnome_hat/main.py", line 11, in <module>
    for x in names:
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/orm/query.py", line 2832, in __iter__
    result = self._iter()
             ^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/orm/query.py", line 2846, in _iter
    result: Union[ScalarResult[_T], Result[_T]] = self.session.execute(
                                                  ^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2246, in execute
    return self._execute_internal(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2141, in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
    result = conn.execute(
             ^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1412, in execute
    return meth(
           ^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 483, in _execute_on_connection
    return connection._execute_clauseelement(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1635, in _execute_clauseelement
    ret = self._execute_context(
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1844, in _execute_context
    return self._exec_single_context(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1984, in _exec_single_context
    self._handle_dbapi_exception(
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2339, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1965, in _exec_single_context
    self.dialect.do_execute(
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 921, in do_execute
    cursor.execute(statement, parameters)
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/cursors.py", line 153, in execute
    result = self._query(query)
             ^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/cursors.py", line 322, in _query
    conn.query(q)
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 558, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 822, in _read_query_result
    result.read()
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 1200, in read
    first_packet = self.connection._read_packet()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/connections.py", line 772, in _read_packet
    packet.raise_for_error()
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/protocol.py", line 221, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "/root/gnome_hat/venv/lib/python3.11/site-packages/pymysql/err.py", line 143, in raise_mysql_exception
    raise errorclass(errno, errval)
sqlalchemy.exc.ProgrammingError: (pymysql.err.ProgrammingError) (1146, "Table 'gnome_hat.stakeholder' doesn't exist")
[SQL: SELECT stakeholder.id AS stakeholder_id, stakeholder.first_name AS stakeholder_first_name, stakeholder.last_name AS stakeholder_last_name, stakeholder.`function` AS stakeholder_function, stakeholder.organisation_id AS stakeholder_organisation_id, stakeholder.national_party_id AS stakeholder_national_party_id, stakeholder.eu_party_id AS stakeholder_eu_party_id, stakeholder.email AS stakeholder_email, stakeholder.website AS stakeholder_website, stakeholder.facebook AS stakeholder_facebook, stakeholder.twitter AS stakeholder_twitter
FROM stakeholder]
(Background on this error at: https://sqlalche.me/e/20/f405)

I have made sure the MySQL server has the relevant database and the user+password are correct


Solution

  • The problem that you have is that you deleted/renamed a migration script somewhere along the way. When you're working in a local environment, this can fly under the radar - the database keeps track of the UUID of the last upgrade, and checks for changes from that point forward.

    It's totally different when you move to a different system; each of the modifications will be made in order. It's a chain of events that will happen, in order from the very first, and you removed a link from that chain.

    Before reading further, if you suspect that this might be the case, it's worth going back through git and trying to find where this happened to restore the file(s).

    There is a nuclear option to fix this (and I would welcome any other answers for a less drastic solution). You basically delete all of the existing migrations and start from scratch; this is the same as adding migrations to an existing database. The trick suggested there is to briefly swap to an in-memory SQLite3 database to generate the new migrations:

    $ DATABASE_URL=sqlite:/// flask db migrate 
    

    You will also need to delete the alembic_version table from your actual database

    This should be cheap and easy to do. However, there are some differences with SQLite3 and most other RDBMS and so I think it would be better if you also included naming conventions:

    from flask_sqlalchemy import SQLAlchemy
    from sqlalchemy import MetaData
    
    meta = MetaData(naming_convention={
            "ix": "ix_%(column_0_label)s",
            "uq": "uq_%(table_name)s_%(column_0_name)s",
            "ck": "ck_%(table_name)s_%(column_0_name)s",
            "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
            "pk": "pk_%(table_name)s"
          })
    
    db = SQLAlchemy(metadata=meta)
    
    def create_app():
        ...
        db.init_app(app)
        ...