问题描述
我正在尝试使用蓝图在烧瓶中创建一个"模块化应用程序".
创建模型时,我遇到了必须引用该应用的问题,以获取Blask-Sqlalchemy提供的db -Object.我希望能够使用多个应用程序使用一些蓝图(类似于如何使用Django应用程序),因此这不是一个好的解决方案.*
- 可以执行switcharoo,并具有蓝图创建db实例,然后该应用程序将其与蓝图其余部分一起导入.但是,任何其他希望创建模型的蓝图都需要从导入蓝图而不是应用程序.
我的问题是:
- 是否有一种方法可以让蓝图定义模型,而没有任何对其后使用的应用程序的意识 - 并有几个蓝图汇集在一起?这样,我的意思是必须从您的蓝图导入应用模块/软件包.
- 我从一开始就错了吗?蓝图不是要独立于该应用程序并重新分布吗?
- 如果不是,那么您应该使用什么模式 来创建类似的东西?烧瓶扩展?您是否应该不这样做 - 也许将所有模型/模式集中在铁轨上?
edit :我现在一直在考虑这个问题,这可能与sqlalchemy相比更重要,因为在声明模型时必须具有declarative_base(). 无论如何,这是必须来自某个地方!
也许最好的解决方案是将您的项目的模式定义在一个地方,并像Ruby上的Rails一样将其散布.声明性sqlalchemy类定义实际上更像是schema.rb,而不是django的models.py.我想这也将使使用迁移更加容易(来自 alembic 或 sqlalchemymigrate ).
我被要求提供一个示例,所以让我们做一些简单的事情:说我有一个描述" 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
这个问题与:
有关和其他各种,但所有答复似乎都依赖于导入应用程序的db实例或进行反向. "大型应用程序如何"在您的蓝图"模式"中导入您的应用程序.
*由于官方文档显示了如何在蓝图中创建路线,视图,模板和资产而不关心它在"中"的应用程序,因此我认为通常应该在应用程序中重新使用蓝图.但是,这种模块性似乎在没有独立模型的情况下都不是有用的.
由于蓝图可以不止一次挂载到应用程序中,因此在蓝图中使用模型的方法可能只是错误的方法?
推荐答案
我相信最真实的答案是,模块化蓝图不应直接关注数据访问,而是依靠提供兼容实现的应用程序.
所以给定示例蓝图.
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的香草dectarative_base,但这应该代表它的要旨.
其他推荐答案
我有类似的需求,即使蓝图完全模块化并且没有引用该应用程序.我想出了一个可能干净的解决方案,但我不确定它是正确的,它的局限性是什么.
这个想法是在蓝图内创建一个单独的db对象(db = SQLAlchemy()),并调用创建root App的init_app()和create_all()方法.
这是一些示例代码,可以显示该项目的结构: 该应用程序称为jobs,蓝图称为status,它存储在蓝图文件夹中.
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
我用gunicorn用gevent工作进行了测试.我在这里问了一个有关解决方案的鲁棒性的单独问题: 创建一个sqlalchemy实例,每个blueprint and Call cal create_all多次
其他推荐答案
您问"蓝图不是要独立于应用程序并重新分布(àladjango应用程序)吗?"
答案是肯定的.蓝图与Django应用不同.
如果要使用不同的应用程序/配置,则需要使用"应用程序调度"而不是蓝图.读这个 [1]: AppDisPatch/#App-dispatch [1]
问题描述
I'm trying to create a "modular application" in Flask using Blueprints.
When creating models, however, I'm running into the problem of having to reference the app in order to get the db-object provided by Flask-SQLAlchemy. I'd like to be able to use some blueprints with more than one app (similar to how Django apps can be used), so this is not a good solution.*
- It's possible to do a switcharoo, and have the Blueprint create the db instance, which the app then imports together with the rest of the blueprint. But then, any other blueprint wishing to create models need to import from that blueprint instead of the app.
My questions are thus:
- Is there a way to let Blueprints define models without any awareness of the app they're being used in later -- and have several Blueprints come together? By this, I mean having to import the app module/package from your Blueprint.
- Am I wrong from the outset? Are Blueprints not meant to be independent of the app and be redistributable (à la Django apps)?
- If not, then what pattern should you use to create something like that? Flask extensions? Should you simply not do it -- and maybe centralize all models/schemas à la Ruby on Rails?
Edit: I've been thinking about this myself now, and this might be more related to SQLAlchemy than Flask because you have to have the declarative_base() when declaring models. And that's got to come from somewhere, anyway!
Perhaps the best solution is to have your project's schema defined in one place and spread it around, like Ruby on Rails does. Declarative SQLAlchemy class definitions are really more like schema.rb than Django's models.py. I imagine this would also make it easier to use migrations (from alembic or sqlalchemy-migrate).
I was asked to provide an example, so let's do something simple: Say I have a blueprint describing "flatpages" -- simple, "static" content stored in the database. It uses a table with just shortname (for URLs), a title and a body. This is 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)
Then, it would be nice to let this blueprint define its own model (this in 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
This question is related to:
And various others, but all replies seem to rely on import the app's db instance, or doing the reverse. The "Large app how to" wiki page also uses the "import your app in your blueprint" pattern.
* Since the official documentation shows how to create routes, views, templates and assets in a Blueprint without caring about what app it's "in", I've assumed that Blueprints should, in general, be reusable across apps. However, this modularity doesn't seem that useful without also having independent models.
Since Blueprints can be hooked into an app more than once, it might simply be the wrong approach to have models in Blueprints?
推荐答案
I believe the truest answer is that modular blueprints shouldn't concern themselves directly with data access, but instead rely on the application providing a compatible implementation.
So given your example blueprint.
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)
From this, there is nothing preventing you from providing a default implementation.
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()
And in your configuration.
app.config["flat_pages.db"] = setup_default_flat_pages_db(db)
The above could be made cleaner by not relying in direct inheritance from db.Model and instead just use a vanilla declarative_base from sqlalchemy, but this should represent the gist of it.
其他推荐答案
I have similar needs of making Blueprints completely modular and having no reference to the App. I came up with a possibly clean solution but I'm not sure how correct it is and what its limitations are.
The idea is to create a separate db object (db = SQLAlchemy()) inside the blueprint and call the init_app() and create_all() methods from where the root app is created.
Here's some sample code to show how the project is structured: The app is called jobs and the blueprint is called status and it is stored inside the blueprints folder.
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
I tested it with gunicorn with gevent worker and it works. I asked a separate question about the robustness of the solution here: Create one SQLAlchemy instance per blueprint and call create_all multiple times
其他推荐答案
You asked "Are Blueprints not meant to be independent of the app and be redistributable (à la Django apps)? "
The answer is yes. Blueprints are not similar to Django App.
If you want to use different app/configurations, then you need to use "Application Dispatching" and not blueprints. Read this [1]: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]
Also, the link here [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]
It clearly says and I quote "A blueprint in Flask is not a pluggable app because it is not actually an application – it’s a set of operations which can be registered on an application, even multiple times. Why not have multiple application objects? You can do that (see Application Dispatching), but your applications will have separate configs and will be managed at the WSGI layer."