在Blueprint模型中使用Flask-SQLAlchemy而不参考应用程序
我正在尝试使用蓝图在Flask中创build一个“模块化应用程序”。
然而,在创build模型时,我遇到了必须引用应用程序以获取由Flask-SQLAlchemy提供的db
-object的问题。 我想能够使用一个以上的应用程序的蓝图(类似于如何使用Django应用程序),所以这不是一个好的解决scheme。
- 有可能做一个switcharoo,并让蓝图创build
db
实例,然后应用程序将其与蓝图的其余部分一起导入。 但是,任何其他希望创build模型的蓝图都需要从蓝图中导入,而不是从应用程序导入。
我的问题是这样的:
- 有没有办法让Blueprints定义模型,而没有意识到他们以后使用的应用程序 – 并且有几个蓝图组合在一起? 通过这个,我的意思是不得不从你的蓝图导入应用程序模块/包。
- 我从一开始就错了吗? 蓝图是不是意味着独立的应用程序和可重新分发(àDjango的应用程序)?
- 如果不是,那么你应该使用什么样的模式来创build类似的东西? 烧瓶扩展? 如果你不这样做 – 也许集中所有模型/模式在Ruby on Rails?
编辑 :我一直在思考这个问题,这可能与Flask更多地涉及SQLAlchemy,因为在声明模型时你必须有
declarative_base()
。 无论如何,这都是从某个地方来的!也许最好的解决scheme是将你的项目模式定义在一个地方,并像Ruby on Rails那样分布。 声明式的SQLAlchemy类定义实际上比schema.rb更像是Django的models.py。 我想这也可以使迁移更容易(从alembic或sqlalchemy迁移 )。
我被要求提供一个例子,所以让我们做一些简单的事情吧:假设我有一个蓝图来描述数据库中存储的简单的“静态”内容。 它使用一个只有短名称(对于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
这个问题涉及到:
- Flask-SQLAlchemy导入/上下文问题
- 什么是你的文件夹布局的Flask应用程序分为模块?
和其他各种,但所有的答复似乎依靠导入应用程序的db
实例,或做相反的事情。 “大型应用程序如何” wiki页面也使用“导入您的应用程序在您的蓝图”模式。
*由于官方文档展示了如何在Blueprint中创build路线,视图,模板和资产,而不必关心它在“什么”的应用程序,所以我认为蓝图应该可以跨应用程序重用。 但是,如果没有独立的模型,这种模块化似乎并不有用。
既然蓝图可以被多次绑定到应用程序中,那么在蓝图中使用模型可能只是错误的方法?
我相信最真实的答案是模块化蓝图不应该直接涉及数据访问,而是依赖于提供兼容实现的应用程序。
所以给你的蓝图。
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)
从这个angular度来看,没有什么能够阻止你提供一个默认的实现。
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()
并在你的configuration。
app.config["flat_pages.db"] = setup_default_flat_pages_db(db)
通过不依赖于db.Model的直接inheritance,上面的代码可以变得更清洁,而只需使用sqlalchemy中的vanilla declarative_base,但这应该代表它的要点。
与实际应用相比,蓝图更多的是关注的逻辑分离。
数据库实例应该由应用程序设置,而不是Blueprint,以允许在一个位置configuration后台处理和其他模块。 如果一个蓝图设置数据库,那么它就是对Flask应用程序其余部分的依赖,使得它不再那么模块化。
查看https://github.com/masom/Bluemonk,了解使用许多不同Flask插件和devise模式的中型Flask应用程序。;
你问:“Blueprints是不是意味着独立于应用程序,并可以重新分发(Django应用程序)?”
答案是肯定的。 蓝图与Django App不相似。
如果你想使用不同的应用程序/configuration,那么你需要使用“应用程序调度”,而不是蓝图。 阅读本文[1]: http : //flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]
另外,这里的链接[1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]
很明显,我引用“Flask中的blueprint不是一个可插入的应用程序,因为它实际上不是一个应用程序 – 它是一组可以在应用程序上注册的操作,甚至可以多次。为什么不能有多个应用程序对象?这样做(请参阅应用程序分派),但您的应用程序将具有单独的configuration,并将在WSGI层进行pipe理。