mal*_*lan 6 python flask flask-sqlalchemy alembic flask-migrate
这个问题已被问过一百万次,但似乎没有一个解决方案对我有用。我应该指出,我在其他项目中经常处理无关紧要的问题
\n目前我正在使用flask-sqlalchemy、flask-migrate 和postgresql。
\n文件结构:
\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 app\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 routes.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 users.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 models\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 annotations.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 mixins.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 users.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 config.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 docker-compose.yml\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 Dockerfile\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 icc2.py <-- the app.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 migrations\nRun Code Online (Sandbox Code Playgroud)\napp/__init__.py
from flask import Flask\nfrom flask_sqlalchemy import SQLAlchemy\nfrom flask_cors import CORS\nfrom elasticsearch import Elasticsearch\nfrom flask_migrate import Migrate\n\nfrom config import Config\n\ndb = SQLAlchemy()\nmigrate = Migrate()\n\ndef create_app(config_class=Config):\n app = Flask(__name__)\n app.config.from_object(config_class)\n\n db.init_app(app)\n migrate.init_app(app, db)\n\n app.es = Elasticsearch([app.config['ELASTICSEARCH_URL']]) \\\n if app.config['ELASTICSEARCH_URL'] else None\n\n from app.main import bp as main_bp\n app.register_blueprint(main_bp, url_prefix='/_api')\n\n CORS(app, resources={r"/_api/*": {"origins": "*"}})\n return app\nRun Code Online (Sandbox Code Playgroud)\nicc2.py
from app import create_app, db\nfrom app.models import classes\n\napp = create_app()\n\n@app.shell_context_processor\ndef make_shell_context():\n print(db)\n return dict(db=db, **classes)\nRun Code Online (Sandbox Code Playgroud)\napp/models/mixins.py
from app import db\n\nfrom sqlalchemy.ext.declarative import declared_attr, as_declarative\n\n@as_declarative()\nclass Base(db.Model):\n """This Base class does nothing. It is here in case I need to expand\n implement something later. I feel like it's a good early practice.\n\n Attributes\n ----------\n id : int\n The basic primary key id number of any class.\n\n Notes\n -----\n The __tablename__ is automatically set to the class name lower-cased.\n There's no need to mess around with underscores, that just confuses the\n issue and makes programmatically referencing the table more difficult.\n """\n __abstract__ = True\n id = db.Column(db.Integer, primary_key=True)\n\n @declared_attr\n def __tablename__(cls):\n return cls.__name__.lower()\n\nRun Code Online (Sandbox Code Playgroud)\napp/models/users.py(annotations.py很相似)
import time\n\nfrom datetime import datetime\n\nfrom app import db\nfrom app.models.mixins import Base\n\nclass User(Base):\n auth0id = db.Column(db.String(64), index=True)\n last_seen = db.Column(db.DateTime, default=datetime.utcnow)\n\n def __repr__(self):\n return f"<User {self.displayname}>"\n\n def __str__(self):\n return self.displayname\nRun Code Online (Sandbox Code Playgroud)\napp/models/__init__.py
import pkgutil\nimport os\nimport importlib\nfrom .mixins import Base\n\npkg_dir = os.path.dirname(__file__)\n\nfor (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):\n importlib.import_module('.' + name, __package__)\n\nclasses = {cls.__name__: cls for cls in Base.__subclasses__()}\nRun Code Online (Sandbox Code Playgroud)\n迭代来自 stackoverflow 代码片段,以便我可以获取类中的模型以公开给 Flask shell 命名空间。
\n我也许不需要将这两个类放在两个单独的文件中,但我的上一个项目最终有 30 个左右的模型,因此组织需要进行一些拆分,所以这只是我开发的一种实践。
\n据我了解,alembic 在生成模型之前需要查看对象的元数据,但在实例化app/__init__.pymigrate 时,数据库还没有引擎。事实上,只是为了测试引擎是如何创建的,我添加了 3 个打印语句来icc2.py打印数据库并查看此时是否有引擎,如下所示:
from app import create_app, db\nfrom app.models import classes\n\napp = create_app()\nprint(db)\n@app.shell_context_processor\ndef make_shell_context():\n print(db)\n return dict(db=db, **classes)\nprint(db)\nRun Code Online (Sandbox Code Playgroud)\n唯一没有显示的打印调用<SQLAlchemy engine=None>是在make_shell_context()函数内。
最后,flask migrate 的输出“初始迁移”:
\nINFO [alembic.runtime.migration] Context impl PostgresqlImpl.\nINFO [alembic.runtime.migration] Will assume transactional DDL.\nINFO [alembic.env] No changes in schema detected.\nRun Code Online (Sandbox Code Playgroud)\n那么如何将我的元数据暴露给flask migrate呢?
\n从 alembic 内的模型模块导入 *env.py对我有用。将所有模型模块与基本模块一起组织为 * 在 a 中导入__init__.py,并从该聚合模块导入 inenv.py也可以。