【问题标题】:Using Flask-SQLAlchemy in Blueprint models without reference to the app [closed]在不参考应用程序的情况下在蓝图模型中使用 Flask-SQLAlchemy [关闭]
【发布时间】:2012-10-15 01:24:48
【问题描述】:

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

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

  • 可以进行切换,让蓝图创建db 实例,然后应用程序将其与蓝图的其余部分一起导入。但是,任何其他希望创建模型的蓝图都需要从那个蓝图而不是应用程序导入。

我的问题是:

  • 有没有一种方法可以让蓝图定义模型,而无需了解它们以后使用的应用程序 - 并且将多个蓝图组合在一起?我的意思是必须从您的蓝图中导入应用模块/包。
  • 我从一开始就错了吗?蓝图不是独立于应用程序并且是可再分发的(à la Django 应用程序)吗?
    • 如果不是,那么应该使用什么模式来创建类似的东西?烧瓶扩展?您是否应该不这样做 - 并且可能集中所有模型/模式 à la Ruby on Rails?

编辑:我自己现在一直在考虑这个问题,这可能与 SQLAlchemy 比 Flask 更相关,因为在声明模型时必须有 declarative_base()。无论如何,那是来自某个地方!

也许最好的解决方案是将项目的模式定义在一个地方并将其分散开,就像 Ruby on Rails 那样。声明式 SQLAlchemy 类定义实际上更像 schema.rb,而不是 Django 的 models.py。我想这也会让迁移的使用变得更容易(来自alembicsqlalchemy-migrate)。


我被要求提供一个示例,所以让我们做一些简单的事情:假设我有一个描述“平面页面”的蓝图——存储在数据库中的简单、“静态”内容。它使用一个只有短名称(用于 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

这个问题与:

还有其他各种,但所有回复似乎都依赖于导入应用程序的 db 实例,或者相反。 "Large app how to" wiki 页面也使用“在您的蓝图中导入您的应用程序”模式。

* 由于官方文档展示了如何在蓝图中创建路由、视图、模板和资产,而不关心它“在”什么应用程序,我假设蓝图通常应该可以跨应用程序重用.但是,如果没有独立的模型,这种模块化似乎没有用。

既然蓝图可以多次挂接到应用程序中,那么在蓝图中添加模型可能就是错误的方法?

【问题讨论】:

  • 您作为可插入应用程序创建的蓝图是否需要定义它们使用的模型?或者它们可以简单地从应用程序中给定模型吗?如果是前者,您能否举例说明您正在尝试创建的一种需要定义自己的模型的可重用蓝图?
  • 我提供了一个简单的例子,就像你问的那样。在这种情况下很难谈论“需要”:如果这是从根本上错误的方法,它不需要定义自己的模型,但我的印象是你“应该”这样做。但是,我可以理解 Flask 是否更适合像 RoR 那样在您的应用程序中定义架构并“提供”给各种插件。我想在那种情况下,您会使用配置来提供它吗?
  • 为什么this solution 在您的情况下不起作用?它不引用模型或蓝图模块中的应用程序。
  • 它仍然依赖于共享命名空间shared.apps,这使得跨应用程序使用相同的蓝图变得很困难(在这样的命名空间上可能没有任何协议)

标签: python flask flask-sqlalchemy


【解决方案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 可以使上述内容更简洁,但这应该代表它的要点。

【讨论】:

  • 非常有趣的拍摄!我不知道这是否是您“应该这样做”的方式,但由于似乎没有一种真正的方法可以解决问题,我会接受您的回答。
  • 这里的答案:stackoverflow.com/a/9695045/6421681 应该和这个一起考虑。这个其他答案不需要导入app,但确实需要导入db;但是,答案中有一个巧妙的解决方法。
【解决方案2】:

我有类似的需求,即使蓝图完全模块化并且不参考应用程序。我想出了一个可能干净的解决方案,但我不确定它的正确性和局限性。

这个想法是在蓝图中创建一个单独的 db 对象 (db = SQLAlchemy()),并从创建根应用程序的位置调用 init_app()create_all() 方法。

这里有一些示例代码来展示项目的结构: 该应用程序名为jobs,蓝图名为status,它存储在blueprints 文件夹中。

blueprints.status.models.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()  # <--- The db object belonging to the blueprint

class Status(db.Model):
    __tablename__ = 'status'
    id = db.Column(db.Integer, primary_key=True)
    job_id = db.Column(db.Integer)
    status = db.Column(db.String(120))

models.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()  # <--- The db object belonging to the root app

class Job(db.Model):
    __tablename__ = 'job'
    id = db.Column(db.Integer, primary_key=True)
    state = db.Column(db.String(120)

factory.py

from .blueprints.status.models import db as status_db  # blueprint db
from .blueprints.status.routes import status_handler   # blueprint handler
from .models import db as root_db                      # root db
from flask import Flask

def create_app():
    app = Flask(__name__)

    # Create database resources.
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////path/to/app.db'
    root_db.init_app(app)
    status_db.init_app(app)     # <--- Init blueprint db object.
    with app.app_context():
        root_db.create_all()
        status_db.create_all()  # <--- Create blueprint db.

    # Register blueprint routes.
    app.register_blueprint(status_handler, url_prefix="/status")

    return app

我用gunicorngevent 工作人员对其进行了测试,它可以工作。我在这里问了一个关于解决方案稳健性的单独问题: Create one SQLAlchemy instance per blueprint and call create_all multiple times

【讨论】:

  • 由于您的单独问题已被删除,您现在还有什么要补充的吗?
【解决方案3】:

您问“蓝图不应该独立于应用程序并且是可再分发的(类似于 Django 应用程序)吗?”

答案是肯定的。蓝图与 Django App 不同。

如果您想使用不同的应用程序/配置,那么您需要使用“应用程序调度”而不是蓝图。读这个 [1]:http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch[1]

另外,这里的链接 [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]

它清楚地表明,我引用“Flask 中的蓝图不是可插入的应用程序,因为它实际上不是一个应用程序——它是一组可以在应用程序上注册的操作,甚至可以多次注册。为什么不拥有多个应用程序对象? 您可以这样做(请参阅应用程序调度),但您的应用程序将具有单独的配置,并将在 WSGI 层进行管理。”

【讨论】:

  • 对,我很欣赏它们与 Django 应用程序不完全一样。但对我来说,这个描述并不是说它们既不是独立的也不是可再发行的。在我看来,蓝图 意味着(通常)可以在多个应用程序上注册。 Django 应用程序也不像 Flask 应用程序——它没有自己的配置或 WSGI 分离。
猜你喜欢
  • 2015-05-04
  • 2019-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-28
  • 2018-02-18
相关资源
最近更新 更多