Как использовать flask-migrate с другими declarative_bases

Я пытаюсь реализовать python-social-auth в Flask. Я сгладил множество перегибов, пытаясь одновременно интерпретировать около 4 руководств и полную книгу Flask, и чувствую, что зашел в тупик с Flask-migrate.

В настоящее время я использую следующий код для создания таблиц, необходимых для работы python-social-auth в среде flask-sqlalchemy.

from social.apps.flask_app.default import models
models.PSABase.metadata.create_all(db.engine)

Теперь они, очевидно, используют какую-то форму своей собственной базы, не связанную с моим фактическим db-объектом. Это, в свою очередь, приводит к тому, что Flask-Migrate полностью пропускает эти таблицы и удаляет их при миграции. Теперь, очевидно, я могу удалить эти db-drops при каждом удалении, но я могу представить, что это одна из тех вещей, о которых в какой-то момент забудут, и внезапно у меня больше не будет связей OAuth.

Я получил это решение для работы с использованием (и модификацией) команды manage.py-command syncdb, как это было предложено пример Flask python-social-auth

Мигель Гринберг, автор Flask-Migrate, отвечает здесь на проблему, которая кажется очень похож на мой.

Самое близкое, что я смог найти при переполнении стека, было это, но оно тоже не исчезает много света на все это для меня, и ответ так и не был принят (и я не могу заставить его работать, я пытался несколько раз)

Для справки, вот мой manage.py:

#!/usr/bin/env python

from flask.ext.script import Server, Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand


from app import app, db

manager = Manager(app)
manager.add_command('runserver', Server())
manager.add_command('shell', Shell(make_context=lambda: {
    'app': app,
    'db_session': db.session
}))

migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)

@manager.command
def syncdb():
    from social.apps.flask_app.default import models
    models.PSABase.metadata.create_all(db.engine)
    db.create_all()

if __name__ == '__main__':
    manager.run()

И чтобы уточнить, команды db init/migration/upgrade создают только мою пользовательскую таблицу (и, очевидно, миграционную), но не таблицы социальной аутентификации, в то время как команда syncdb работает для таблиц python-social-auth.

Из ответа github я понимаю, что это не поддерживается Flask-Migrate, но мне интересно, есть ли способ возиться с PSABase-таблицами, чтобы они были подобраны объектом db, отправленным в Migrate.

Любые предложения приветствуются.

(Кроме того, постер впервые. Я чувствую, что провел много исследований и попробовал довольно много решений, прежде чем наконец пришел сюда, чтобы опубликовать. Если я пропустил что-то очевидное в рекомендациях SO, не стесняйтесь укажите это мне в личном сообщении, и я с радостью обязуюсь)


person Sjolus    schedule 03.02.2016    source источник


Ответы (3)


Проблема в том, что у вас есть два набора моделей, каждый с другим объектом метаданных SQLAlchemy. Модели из PSA были сгенерированы непосредственно из SQLAlchemy, а ваши собственные модели были сгенерированы с помощью Flask-SQLAlchemy.

Flask-Migrate видит только модели, определенные с помощью Flask-SQLAlchemy, потому что объект db, который вы ему даете, знает только о метаданных для этих моделей, он ничего не знает об этих других моделях PSA, которые обходят Flask-SQLAlchemy.

Итак, конечным результатом является то, что каждый раз, когда вы создаете миграцию, Flask-Migrate/Alembic находит эти таблицы PSA в базе данных и решает их удалить, потому что не видит для них никаких моделей.

Я думаю, что лучшее решение вашей проблемы — настроить Alembic так, чтобы он игнорировал определенные таблицы. Для этого вы можете использовать include_object в модуле env.py, хранящемся в каталоге миграции. По сути, вы собираетесь написать функцию, которую Alembic будет вызывать каждый раз, когда сталкивается с новым объектом при создании сценария миграции. Функция вернет False, если рассматриваемый объект является одной из этих таблиц PSA, и True во всех остальных случаях.

Обновление. Другой вариант, который вы включили в ответ, который вы написали, состоит в том, чтобы объединить два объекта метаданных в один, после чего модели из вашего приложения и PSA проверяются Alembic вместе.

Я ничего не имею против метода слияния нескольких объектов метаданных в один, но я думаю, что приложение не должно отслеживать миграции в моделях, которые не принадлежат вам. Во многих случаях Alembic не сможет точно зафиксировать миграцию, поэтому вам может потребоваться внести небольшие исправления в сгенерированный скрипт перед его применением. Для ваших моделей вы можете обнаружить эти неточности, которые иногда проявляются в сценариях миграции, но когда модели не ваши, я думаю, вы можете что-то упустить, потому что вы не будете достаточно знакомы с изменениями, внесенными в эти моделей, чтобы сделать хороший обзор сгенерированного скрипта Alembic.

По этой причине я думаю, что лучше использовать предложенную мной конфигурацию include_object, чтобы исключить сторонние модели из ваших миграций. Вместо этого эти модели следует перенести в соответствии с инструкциями стороннего проекта.

person Miguel    schedule 04.02.2016
comment
Спасибо за ответ, Мигель. Точно, чуть не забыл про Alembic. Разве я не мог просто пропустить Flask-Migrate и использовать его напрямую? Мне кажется, что его можно обмануть, заставив думать, что это установка с несколькими базами данных (что, по сути, так и есть с точки зрения Alembics). - person Sjolus; 04.02.2016
comment
Хм, не уверен, что вы получите другие результаты с Alembic. Если вы настроите конфигурацию с несколькими базами данных (которую вы также можете настроить с помощью Flask-Migrate, кстати), проблема будет проявляться с обеих сторон. Миграции для вашей обычной базы данных по-прежнему будут пытаться удалить модели PSA, но в дополнение к этому миграции для стороны PSA также попытаются удалить ваши таблицы. Один из способов избежать этого бардака — использовать две отдельные базы данных, тогда таблицы с одной стороны не будут видны другой. - person Miguel; 04.02.2016
comment
Ах, да, они будут противоречить друг другу. Было бы не так уж плохо с отдельными базами данных, но я думаю, что пользователи могут легко пропустить их во время устранения неполадок. Я думаю, я мог бы просто реализовать материал социальной аутентификации самостоятельно. Мне просто немного грустно, что я так близок к финишу. Все работает, за исключением миграций, которые являются обязательными, поэтому мне приходится снова все вырывать и начинать заново :P - person Sjolus; 04.02.2016
comment
В чем проблема с опцией include_object? Это должно решить вашу проблему, исключив модели PSA из оценщика миграции. - person Miguel; 04.02.2016
comment
Не подвергну ли я их исключению из-за изменений в моделях PSA, которые не будут перенесены, или же я подвергну себя риску, включив их? Разве средство миграции не обнаружит изменения в моделях PSA теперь, когда я их правильно включил (мой собственный ответ)? - person Sjolus; 04.02.2016
comment
Спасибо за редактирование, Мигель, это проясняет, что вы имеете в виду. Принятие этого ответа, поскольку он действительно привел меня ко всем выводам и решениям, которые мне действительно были нужны. - person Sjolus; 05.02.2016

После полезного ответа Мигеля здесь я получил несколько новых ключевых слов для исследования. Я оказался на полезной странице github. в котором содержались дополнительные ссылки, среди прочего, на битбакет Alembic сайт, который очень помог.

В конце концов я сделал это с моим env.py-файлом миграции Alembic:

from sqlalchemy import engine_from_config, pool, MetaData

[...]

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from flask import current_app
config.set_main_option('sqlalchemy.url',
                       current_app.config.get('SQLALCHEMY_DATABASE_URI'))

def combine_metadata(*args):
    m = MetaData()
    for metadata in args:
        for t in metadata.tables.values():
            t.tometadata(m)
    return m

from social.apps.flask_app.default import models

target_metadata = combine_metadata(
    current_app.extensions['migrate'].db.metadata,
    models.PSABase.metadata)

Кажется, это работает абсолютно идеально.

person Sjolus    schedule 04.02.2016
comment
Я подтверждаю, что это должен быть принятый ответ в этом вопросе и ответе. Мне было беспомощно необходимо удалить инструкцию drop/create для всех таблиц социальной аутентификации в файле миграции обновления/понижения. Имея указанное выше исправление, оба обновления/отката изначально будут пустыми. Безупречное решение :) - person swdev; 09.09.2016

Я использую две модели следующим образом: -

Тот, который используется с использованием БД как

db = SQLAlchemy()
app['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:' + POSTGRES_PASSWORD + '@localhost/Flask'
db.init_app(app)

class User(db.Model):
    pass

другой с Базой как

Base = declarative_base()
uri = 'postgresql://postgres:' + POSTGRES_PASSWORD + '@localhost/Flask'
engine = create_engine(uri)
metadata = MetaData(engine)
Session = sessionmaker(bind=engine)
session = Session()

class Address(Base):
    pass

Поскольку вы создали пользователя с помощью db.Model, вы можете использовать миграцию фляги для пользователя и адреса класса, используемого в базе данных, который обрабатывает выборку уже существующей таблицы из базы данных.

person Saif ali Karedia    schedule 06.05.2017