如何让Flask-Migrate读取当前的应用配置?



我运行flask db migrate有问题。我已经运行flask db init没有问题,并修改了结果migrations/env.py文件,只关注现有MS SQL Server数据库中的特定模式。它的行为就像migrations/env.py: run_migrations_online()方法没有找到正确的应用程序配置。它想在内存db中创建一个默认的sqlite。

我在create_app()中定义SQLALCHEMY_DATABASE_URI,在本地配置文件中读取:

app.config.from_object('app.local_settings')

mssql数据库的配置应该是:

SQLALCHEMY_DATABASE_URI = 
"mssql+pyodbc://db-dev.my.company.com/devdb?driver=SQL+Server"

我的应用程序入口点看起来像:

from app import create_app
try:
app = create_app()
except Exception as e:
print(repr(e))
raise
if __name__ == "__main__":
try:
app.run(debug=True)
except Exception as e:
app.logger.error(repr(e))
print(repr(e))

create_app()的定义在app模块的__init__.py中:

import pyodbc
from flask import Flask
from flask_mail import Mail
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect
from flask_security import Security, SQLAlchemyUserDatastore, auth_required, 
hash_password
from flask_security.models import fsqla_v2 as fsqla
# Instantiate Flask
app = Flask(__name__)
# Instantiate Flask extensions
csrf_protect = CSRFProtect(app=app)
db = SQLAlchemy(app=app)
mail = Mail(app=app)
migrate = Migrate(app=app, db=db)

# Initialize Flask Application
def create_app(extra_config_settings={}):
# Load common settings
app.config.from_object('app.settings')
# Load environment specific settings
app.config.from_object('app.local_settings')
# Load extra settings from extra_config_settings param
app.config.update(extra_config_settings)
# print(pyodbc.drivers())
# Setup Flask-SQLAlchemy
db.init_app(app)
# Setup Flask-Migrate
migrate.init_app(app=app, db=db)
# Setup Flask-Mail
mail.init_app(app)
# Setup WTForms CSRFProtect
csrf_protect.init_app(app)
# Register blueprints
from .views import register_blueprints
register_blueprints(app)
# Setup an error-logger to send emails to app.config.ADMINS
init_email_error_handler(app)
# Setup Flask-User to handle user account related forms
from .models.user import User, UserRegisterForm, UserProfileForm, 
UserLoginForm
from .views.main import user_profile_page
from .models.roles import Role
# APIs
from .views import register_api, SchoolAPI
register_api(app, SchoolAPI, 'school_api', '/school/', pk='school_id')
# @app.context_processor
# def context_processor():
#   return dict(user_manager=user_manager)
fsqla.FsModels.set_db_info(db)
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
return app

我已将migrations/env.py文件修改为:

from __future__ import with_statement
import logging
from logging.config import fileConfig
from flask import current_app
from alembic import context
config = context.config
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
config.set_main_option(
'sqlalchemy.url',
str(current_app.extensions['migrate'].db.get_engine().url).replace(
'%', '%%'))
target_metadata = current_app.extensions['migrate'].db.metadata

def include_name(name, type_, parent_names):
result = False
if type_ == "schema":
return name in ["MySchema"]
else:
return True

def run_migrations_offline():
"""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")
print(f'run_migrations_offline: {url}')
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
version_table_schema='MySchema',
include_schemas=True,
include_name=include_name
)
with context.begin_transaction():
context.run_migrations()

def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
# this callback is used to prevent an auto-migration from being generated
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
connectable = current_app.extensions['migrate'].db.get_engine()
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
version_table_schema='MySchema',
include_schemas=True,
include_name=include_name,
**current_app.extensions['migrate'].configure_args
)
with context.begin_transaction():
context.run_migrations()

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

当我运行flask db migrate时,我得到:

H:pathtomyprojectUserInterfacevenvlibsite-packagesflask_sqlalchemy__init__.py:851: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
warnings.warn(
H:pathtomyprojectUserInterfacevenvlibsite-packagesflask_sqlalchemy__init__.py:872: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds signi
ficant overhead and will be disabled by default in the future.  Set it to True or False to suppress this warning.
warnings.warn(FSADeprecationWarning(
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
Traceback (most recent call last):
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemyenginebase.py", line 1819, in _execute_context
self.dialect.do_execute(
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemyenginedefault.py", line 732, in do_execute
cursor.execute(statement, parameters)
sqlite3.OperationalError: unknown database "MyProjectDB"
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:Python310librunpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:Python310librunpy.py", line 86, in _run_code
exec(code, run_globals)
File "H:pathtomyprojectUserInterfacevenvScriptsflask.exe__main__.py", line 7, in <module>
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesflaskcli.py", line 988, in main
cli.main()
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesflaskcli.py", line 579, in main
return super().main(*args, **kwargs)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickcore.py", line 1055, in main
rv = self.invoke(ctx)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickcore.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickcore.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickcore.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickcore.py", line 760, in invoke
return __callback(*args, **kwargs)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickdecorators.py", line 26, in new_func
return f(get_current_context(), *args, **kwargs)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesflaskcli.py", line 427, in decorator
return __ctx.invoke(f, *args, **kwargs)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesclickcore.py", line 760, in invoke
return __callback(*args, **kwargs)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesflask_migratecli.py", line 104, in migrate
_migrate(directory, message, sql, head, splice, branch_label, version_path,
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesflask_migrate__init__.py", line 98, in wrapped
f(*args, **kwargs)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesflask_migrate__init__.py", line 155, in migrate
command.revision(config, message, autogenerate=True, sql=sql,
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembiccommand.py", line 229, in revision
script_directory.run_env()
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicscriptbase.py", line 569, in run_env
util.load_python_file(self.dir, "env.py")
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicutilpyfiles.py", line 94, in load_python_file
module = load_module_py(module_id, path)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicutilpyfiles.py", line 110, in load_module_py
spec.loader.exec_module(module)  # type: ignore
File "<frozen importlib._bootstrap_external>", line 883, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "H:pathtomyprojectUserInterfaceMyProjectDBmigrationsenv.py", line 108, in <module>
run_migrations_online()
File "H:pathtomyprojectUserInterfaceMyProjectDBmigrationsenv.py", line 102, in run_migrations_online
context.run_migrations()
File "<string>", line 8, in run_migrations
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicruntimeenvironment.py", line 853, in run_migrations
self.get_context().run_migrations(**kw)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicruntimemigration.py", line 601, in run_migrations
heads = self.get_current_heads()
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicruntimemigration.py", line 533, in get_current_heads
if not self._has_version_table():
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicruntimemigration.py", line 549, in _has_version_table
return sqla_compat._connectable_has_table(
File "H:pathtomyprojectUserInterfacevenvlibsite-packagesalembicutilsqla_compat.py", line 195, in _connectable_has_table
return inspect(connectable).has_table(tablename, schemaname)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemyenginereflection.py", line 283, in has_table
return self.dialect.has_table(conn, table_name, schema)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemydialectssqlitebase.py", line 2018, in has_table
info = self._get_table_pragma(
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemydialectssqlitebase.py", line 2545, in _get_table_pragma
cursor = connection.exec_driver_sql(statement)
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemyenginebase.py", line 1686, in exec_driver_sql
return self._exec_driver_sql(
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemyenginebase.py", line 1595, in _exec_driver_sql
ret = self._execute_context(
File "H:pathtomyprojectUserInterfacevenvlibsite-packagessqlalchemyenginebase.py", line 1862, in _execute_context

想法吗?

原来我是在尝试遵循Flask-Security-Too的"基本SQLAlchemy应用程序"。快速入门指南。我不是说这是不正确的,但我的理解可能有缺陷。

我决定替换FST的"会话"。概念与更简单的SQLAlchemy应用程序样品和一切都很顺利。

我对一般问题的最好猜测是,也许Alembic不是在正确的版本,或者env.py文件有变化,我无法弄清楚。

最新更新