首页 > 解决方案 > Flask-Migrate/Alembic 未使用基类和多文件结构检测模型

问题描述

这个问题已经被问了一百万次,但似乎没有一个解决方案对我有用。我应该注意到我在其他项目中经常处理切线问题

目前我正在使用 flask-sqlalchemy、flask-migrate 和 postgresql。

文件结构:

├── app
│   ├── __init__.py
│   ├── main
│   │   ├── __init__.py
│   │   ├── routes.py
│   │   └── users.py
│   ├── models
│   │   ├── annotations.py
│   │   ├── __init__.py
│   │   ├── mixins.py
│   │   └── users.py
├── config.py
├── docker-compose.yml
├── Dockerfile
├── icc2.py           <-- the app.py
├── migrations

app/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from elasticsearch import Elasticsearch
from flask_migrate import Migrate

from config import Config

db = SQLAlchemy()
migrate = Migrate()

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)

    db.init_app(app)
    migrate.init_app(app, db)

    app.es = Elasticsearch([app.config['ELASTICSEARCH_URL']]) \
        if app.config['ELASTICSEARCH_URL'] else None

    from app.main import bp as main_bp
    app.register_blueprint(main_bp, url_prefix='/_api')

    CORS(app, resources={r"/_api/*": {"origins": "*"}})
    return app

icc2.py

from app import create_app, db
from app.models import classes

app = create_app()

@app.shell_context_processor
def make_shell_context():
    print(db)
    return dict(db=db, **classes)

app/models/mixins.py

from app import db

from sqlalchemy.ext.declarative import declared_attr, as_declarative

@as_declarative()
class Base(db.Model):
    """This Base class does nothing. It is here in case I need to expand
    implement something later. I feel like it's a good early practice.

    Attributes
    ----------
    id : int
        The basic primary key id number of any class.

    Notes
    -----
    The __tablename__ is automatically set to the class name lower-cased.
    There's no need to mess around with underscores, that just confuses the
    issue and makes programmatically referencing the table more difficult.
    """
    __abstract__ = True
    id = db.Column(db.Integer, primary_key=True)

    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

app/models/users.pyannotations.py类似)

import time

from datetime import datetime

from app import db
from app.models.mixins import Base

class User(Base):
    auth0id = db.Column(db.String(64), index=True)
    last_seen = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return f"<User {self.displayname}>"

    def __str__(self):
        return self.displayname

app/models/__init__.py

import pkgutil
import os
import importlib
from .mixins import Base

pkg_dir = os.path.dirname(__file__)

for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]):
    importlib.import_module('.' + name, __package__)

classes = {cls.__name__: cls for cls in Base.__subclasses__()}

迭代来自一个 stackoverflow 片段,这样我就可以让一个类中的模型暴露给烧瓶外壳命名空间。

我可能不需要将这两个类放在两个单独的文件中,但我的上一个项目最终有 30 个左右的模型,因此组织需要进行一些拆分,所以这只是我开发的一种实践。

据我了解,alembic 需要先查看对象的元数据,然后才能生成模型,但是在app/__init__.py实例化 migrate 时,数据库还没有引擎。事实上,只是为了测试引擎是如何创建的,我添加了 3 个打印语句来icc2.py打印 db 并查看它是否有引擎,如下所示:

from app import create_app, db
from app.models import classes

app = create_app()
print(db)
@app.shell_context_processor
def make_shell_context():
    print(db)
    return dict(db=db, **classes)
print(db)

唯一没有显示的打印调用<SQLAlchemy engine=None>make_shell_context()函数内。

最后,flask migrate “初始迁移”的输出:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.env] No changes in schema detected.

那么如何将我的元数据公开给烧瓶迁移呢?

标签: pythonflaskflask-sqlalchemyalembicflask-migrate

解决方案


推荐阅读