一尘不染

在不参考应用程序的情况下在蓝图模型中使用Flask-SQLAlchemy

flask

我正在尝试使用蓝图在Flask中创建一个“模块化应用程序”。

但是,在创建模型时,我遇到了必须引用该应用程序才能获取dbFlask-SQLAlchemy提供的-object的问题。我希望能够在多个应用程序中使用一些蓝图(类似于Django应用程序的使用方式),所以这不是一个好的解决方案。*

  • 可以进行切换,让蓝图创建db实例,然后由应用程序将其与其余的蓝图一起导入。但是随后,任何其他希望创建模型的蓝图都需要从该蓝图而不是应用程序中导入。
    因此,我的问题是:

  • 有没有一种方法可以让蓝图定义模型,而又不知道它们以后将使用的应用程序-并且有多个蓝图组合在一起?我的意思是必须从你的蓝图导入应用程序模块/软件包。

  • 我从一开始就错了吗?蓝图是否不是要独立于应用程序并可以重新分发(例如Django应用程序)?
    如果没有,那么你应该使用什么模式来创建类似的东西?烧瓶扩展?你是否应该简单地做到这一点-也许集中所有模型/方案以及Ruby on Rails?
    编辑:我现在一直在考虑这个问题,这可能与SQLAlchemy而不是Flask有关,因为declarative_base()在声明模型时必须具有。而这得从什么地方来的,反正!

也许最好的解决方案是像Ruby on Rails一样,将项目的模式定义在一个位置并进行分散。声明性SQLAlchemy类定义实际上更像是schema.rb,而不是Django的models.py。我想这也将使使用迁移(从Alembic或sqlalchemy-migrate)更容易。

我被要求提供一个示例,所以让我们做一些简单的事情:假设我有一个描述“ flatpages”的蓝图,这是存储在数据库中的简单,“静态”内容。它使用仅包含短名称(用于URL),标题和正文的表。这是simple_pages/__init__.py

from flask import Blueprint, render_template
from .models import Page

flat_pages = Blueprint('flat_pages', __name__, template_folder='templates')

@flat_pages.route('/<page>')
def show(page):
    page_object = Page.query.filter_by(name=page).first()
    return render_template('pages/{}.html'.format(page), page=page_object)

然后,最好让这个蓝图定义自己的模型(位于中simple_page/models.py):

# TODO Somehow get ahold of a `db` instance without referencing the app
# I might get used in!

class Page(db.Model):
    name = db.Column(db.String(255), primary_key=True)
    title = db.Column(db.String(255))
    content = db.Column(db.String(255))

    def __init__(self, name, title, content):
        self.name = name
        self.title = title
        self.content = content

阅读 661

收藏
2020-04-06

共1个答案

一尘不染

我相信最真实的答案是模块化蓝图不应该直接与数据访问相关,而应该依赖于提供兼容实现的应用程序。

因此,给出你的示例蓝图。

from flask import current_app, Blueprint, render_template

flat_pages = Blueprint('flat_pages', __name__, template_folder='templates')

@flat_pages.record
def record(state):
    db = state.app.config.get("flat_pages.db")

    if db is None:
        raise Exception("This blueprint expects you to provide "
                        "database access through flat_pages.db")

@flat_pages.route('/<page>')
def show(page):
    db = current_app.config["flat_pages.db"]
    page_object = db.find_page_by_name(page)
    return render_template('pages/{}.html'.format(page), page=page_object)

因此,没有什么可以阻止你提供默认实现的。

def setup_default_flat_pages_db(db):
    class Page(db.Model):
        name = db.Column(db.String(255), primary_key=True)
        title = db.Column(db.String(255))
        content = db.Column(db.String(255))

        def __init__(self, name, title, content):
            self.name = name
            self.title = title
            self.content = content

    class FlatPagesDBO(object):
        def find_page_by_name(self, name):
            return Page.query.filter_by(name=name).first()

    return FlatPagesDBO()

并在你的配置中。

app.config["flat_pages.db"] = setup_default_flat_pages_db(db)

通过不依赖于db.Model的直接继承,可以使上述内容更简洁,而仅使用来自sqlalchemy的vanilla declarative_base,但这应该代表了它的要旨。

2020-04-06