`flask db upgrade` работает нормально, но последующие вызовы` flask db migrate` удаляют все таблицы - PullRequest
0 голосов
/ 07 мая 2020

В какой-то момент я смог использовать flask db CLI для генерации моей начальной миграции модели данных. Мое приложение Dockerized, и когда я запускаю его, я вызываю flask db upgrade в entrypoint.sh, что выдает:

backend_1   | INFO  [alembic.runtime.migration] Context impl MySQLImpl.
backend_1   | INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
backend_1   | INFO  [alembic.runtime.migration] Running upgrade  -> 7f973d2726ef, initial data model

Так что это здорово. Теперь я внес обновления в модель данных и хочу создать миграцию, точно так же, как я мог раньше, с помощью flask db migrate -m 'update games tables', который отображает:

INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected removed table 'games'
INFO  [alembic.autogenerate.compare] Detected removed table 'game_status'
INFO  [alembic.autogenerate.compare] Detected removed table 'positions'
INFO  [alembic.autogenerate.compare] Detected removed table 'users'
INFO  [alembic.autogenerate.compare] Detected removed table 'game_invites'
  Generating /home/backend/database/migrations/versions/55f785302c18_update_games_tables.py ...  done

Моя flask db migrate команда видит таблицы базы данных , но поскольку он не читает никаких данных модели, он думает, что мы хотим удалить их все. Я действительно озадачен этим. Последний пост, который я просмотрел, был этот . Согласно предложениям Мигеля по устранению неполадок в сообщении, у меня есть:

  • Подтверждено, что я использую один и тот же db во всем контексте приложения (см. Ниже)
  • Явно импортировал все модели данных перед вызовом migrate.init_app
  • Разрушил и перестроил мою базу данных несколько раз, включая перезапуск каталога миграции с flask db init

Моя структура каталогов выглядит так, и я Я включу то, что я считаю наиболее важными фрагментами кода ниже:

/backend/
|       |__/__init__.py (empty)
|       |__/app_factory.py
|       |__/backend-entrypoint.sh
|       |__/wsgi.py
|       |__/config.py
|       |__/.flaskenv
|
|__/api (nothing important here)
|
|__/database/
|           |__/migrations/... (all the usual stuff here)
|           |__/db.py
|           |__/models.py
|
|__/tests (nothing important here)

db.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy() # <- This is the db that's going to get passed around

models.py

from aenum import Enum

from backend.database.db import db


class Users(db.Model):
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Text)
    email = db.Column(db.Text)
    profile_pic = db.Column(db.Text)
    username = db.Column(db.Text)
    created_at = db.Column(db.DATETIME)
...
# Here's the part where I'm making changes to the schema.

app_factory.py

from flask import Flask
from flask_migrate import Migrate

from database.db import db
from api import routes as routes


def create_app():
    app = Flask(__name__)
    app.config.from_object('config.Config')
    db.init_app(app)
    migrate = Migrate()

    with app.app_context():
        app.register_blueprint(routes.routes)
        # It's annoying that flask works this way. migrate and models both become part of the application context here
        from backend.database.models import (
            Users,
            Games,
            GameStatus,
            GameInvites,
            Positions
        )
        migrate.init_app(app, db, directory="/home/backend/database/migrations")
        return app

серверная точка входа. sh

#!/usr/bin/env bash
until nc -z -v -w30 $DB_HOST 3306
do
  echo "Waiting a second until the database is receiving connections..."
  sleep 1
done

flask db upgrade # construct the data model

python wsgi.py # start the web application

wsgi.py

from backend.app_factory import create_app

from config import Config

app = create_app()


if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=Config.DEBUG_MODE, ssl_context=("cert.pem", "key.pem"))

config.py

...
class Config:
    ...
    SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}?charset=utf8"
...

.flaskenv

FLASK_APP=/home/backend/wsgi.py

Это сработало в какой-то момент, но ничего из того, что я пробовал сегодня, включая разрушение всей базы данных, восстановление контейнера и начало с нуля с помощью flask db init, не дало мне никаких где. Проблема не в том, что изменения не обнаруживаются, а в том, что flask db даже не подключается к нужной базе данных, хотя я установил Migrate в контексте приложения flask. У меня есть функциональные тесты, которые я запускаю для сервера API, на котором работает приложение flask, и у него нет проблем с взаимодействием с заполненной базой данных. Проблема кажется довольно ограниченной для flask db CLI. Я попытался вернуться к более ранним версиям, чтобы узнать, смогу ли я определить, повредило ли что-то изменение в моей файловой структуре, но эта проблема сохраняется при нескольких коммитах.

1 Ответ

0 голосов
/ 07 мая 2020

Разобрался, просто очистив макет create_app.py. Спасибо @Miguel за указатели по этому поводу. Проблема, похоже, заключалась в 1) неправильном использовании app_context и 2) порядке импорта models. Когда я вырезаю строку with app.app_context() и перемещаю импорт модели вниз, я получаю то, что отлично работает:

from flask import Flask
from flask_migrate import Migrate

from backend.database.db import db
from backend.api import routes as routes
from config import Config


def create_app():
    app = Flask(__name__)
    app.config.from_object('config.Config')
    app.register_blueprint(routes.routes)
    db.init_app(app)
    migrate = Migrate(app, db, directory=Config.MIGRATIONS_DIRECTORY)
    migrate.init_app(app, db)
    return app

from backend.database import models

Я использовал файл __init__ из Flask Mega Tutorial Мигеля. для этого. Двумя основными отличиями моей архитектуры от его архитектуры являются то, что я создаю свой объект db в отдельном файле (см. Выше), чтобы избежать циклического импорта при использовании в другом месте (например, в моих определениях маршрутов api), и я определяю create_app.py файл, а не помещать все настройки приложения в __init__

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...