forked from orbit-oss/flask
Started documentation for blueprints
This commit is contained in:
parent
b4ffa4d9ac
commit
bfd67764fb
4 changed files with 94 additions and 30 deletions
43
docs/blueprints.rst
Normal file
43
docs/blueprints.rst
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
.. _blueprints:
|
||||
|
||||
Modular Applications with Blueprints
|
||||
====================================
|
||||
|
||||
.. versionadded:: 0.7
|
||||
|
||||
Flask knows a concept known as “blueprints” which can greatly simplify how
|
||||
large applications work. A blueprint is an object works similar to an
|
||||
actual :class:`Flask` application object, but it is not actually an
|
||||
application. Rather it is the blueprint of how to create an application.
|
||||
Think of it like that: you might want to have an application that has a
|
||||
wiki. So what you can do is creating the blueprint for a wiki and then
|
||||
let the application assemble the wiki on the application object.
|
||||
|
||||
Why Blueprints?
|
||||
---------------
|
||||
|
||||
Why have blueprints and not multiple application objects? The utopia of
|
||||
pluggable applications are different WSGI applications and merging them
|
||||
together somehow. You can do that (see :ref:`app-dispatch`) but it's not
|
||||
the right tool for every case. Having different applications means having
|
||||
different configs. Applications are also separated on the WSGI layer
|
||||
which is a lot lower level than the level that Flask usually operates on
|
||||
where you have request and response objects.
|
||||
|
||||
Blueprints do not necessarily have to implement applications. They could
|
||||
only provide filters for templates, static files, templates or similar
|
||||
things. They share the same config as the application and can change the
|
||||
application as necessary when being registered.
|
||||
|
||||
The downside is that you cannot unregister a blueprint once application
|
||||
without having to destroy the whole application object.
|
||||
|
||||
The Concept of Blueprints
|
||||
-------------------------
|
||||
|
||||
The basic concept of blueprints is that they record operations that should
|
||||
be executed when the blueprint is registered on the application. However
|
||||
additionally each time a request gets dispatched to a view that was
|
||||
declared to a blueprint Flask will remember that the request was
|
||||
dispatched to that blueprint. That way it's easier to generate URLs from
|
||||
one endpoint to another in the same module.
|
||||
|
|
@ -18,6 +18,7 @@ instructions for web development with Flask.
|
|||
config
|
||||
signals
|
||||
reqcontext
|
||||
blueprints
|
||||
shell
|
||||
patterns/index
|
||||
deploying/index
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ class Blueprint(_PackageBoundObject):
|
|||
.. versionadded:: 0.7
|
||||
"""
|
||||
|
||||
warn_on_modifications = False
|
||||
_got_registered_once = False
|
||||
|
||||
def __init__(self, name, import_name, static_folder=None,
|
||||
static_url_path=None, url_prefix=None,
|
||||
subdomain=None):
|
||||
|
|
@ -64,14 +67,29 @@ class Blueprint(_PackageBoundObject):
|
|||
self.deferred_functions = []
|
||||
self.view_functions = {}
|
||||
|
||||
def _record(self, func):
|
||||
def record(self, func):
|
||||
"""Registers a function that is called when the blueprint is
|
||||
registered on the application. This function is called with the
|
||||
state as argument as returned by the :meth:`make_setup_state`
|
||||
method.
|
||||
"""
|
||||
if self._got_registered_once and self.warn_on_modifications:
|
||||
from warnings import warn
|
||||
warn(Warning('The blueprint was already registered once '
|
||||
'but is getting modified now. These changes '
|
||||
'will not show up.'))
|
||||
self.deferred_functions.append(func)
|
||||
|
||||
def _record_once(self, func):
|
||||
def record_once(self, func):
|
||||
"""Works like :meth:`record` but wraps the function in another
|
||||
function that will ensure the function is only called once. If the
|
||||
blueprint is registered a second time on the application, the
|
||||
function passed is not called.
|
||||
"""
|
||||
def wrapper(state):
|
||||
if state.first_registration:
|
||||
func(state)
|
||||
return self._record(update_wrapper(wrapper, func))
|
||||
return self.record(update_wrapper(wrapper, func))
|
||||
|
||||
def make_setup_state(self, app, options, first_registration=False):
|
||||
return BlueprintSetupState(self, app, options, first_registration)
|
||||
|
|
@ -83,6 +101,7 @@ class Blueprint(_PackageBoundObject):
|
|||
:func:`~flask.Flask.register_blueprint` are directly forwarded to this
|
||||
method in the `options` dictionary.
|
||||
"""
|
||||
self._got_registered_once = True
|
||||
state = self.make_setup_state(app, options, first_registration)
|
||||
if self.has_static_folder:
|
||||
state.add_url_rule(self.static_url_path + '/<path:filename>',
|
||||
|
|
@ -105,7 +124,7 @@ class Blueprint(_PackageBoundObject):
|
|||
"""Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
|
||||
the :func:`url_for` function is prefixed with the name of the module.
|
||||
"""
|
||||
self._record(lambda s:
|
||||
self.record(lambda s:
|
||||
s.add_url_rule(rule, endpoint, view_func, **options))
|
||||
|
||||
def endpoint(self, endpoint):
|
||||
|
|
@ -118,7 +137,7 @@ class Blueprint(_PackageBoundObject):
|
|||
def decorator(f):
|
||||
def register_endpoint(state):
|
||||
state.app.view_functions[endpoint] = f
|
||||
self._record_once(register_endpoint)
|
||||
self.record_once(register_endpoint)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
|
|
@ -127,7 +146,7 @@ class Blueprint(_PackageBoundObject):
|
|||
is only executed before each request that is handled by a function of
|
||||
that module.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.before_request_funcs
|
||||
self.record_once(lambda s: s.app.before_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -135,7 +154,7 @@ class Blueprint(_PackageBoundObject):
|
|||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||
before each request, even if outside of a module.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.before_request_funcs
|
||||
self.record_once(lambda s: s.app.before_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -144,7 +163,7 @@ class Blueprint(_PackageBoundObject):
|
|||
is only executed after each request that is handled by a function of
|
||||
that module.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.after_request_funcs
|
||||
self.record_once(lambda s: s.app.after_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -152,7 +171,7 @@ class Blueprint(_PackageBoundObject):
|
|||
"""Like :meth:`Flask.after_request` but for a module. Such a function
|
||||
is executed after each request, even if outside of the module.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.after_request_funcs
|
||||
self.record_once(lambda s: s.app.after_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -160,7 +179,7 @@ class Blueprint(_PackageBoundObject):
|
|||
"""Like :meth:`Flask.context_processor` but for a module. This
|
||||
function is only executed for requests handled by a module.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.template_context_processors
|
||||
self.record_once(lambda s: s.app.template_context_processors
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -168,7 +187,7 @@ class Blueprint(_PackageBoundObject):
|
|||
"""Like :meth:`Flask.context_processor` but for a module. Such a
|
||||
function is executed each request, even if outside of the module.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.template_context_processors
|
||||
self.record_once(lambda s: s.app.template_context_processors
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -177,7 +196,7 @@ class Blueprint(_PackageBoundObject):
|
|||
handler is used for all requests, even if outside of the module.
|
||||
"""
|
||||
def decorator(f):
|
||||
self._record_once(lambda s: s.app.errorhandler(code)(f))
|
||||
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||
return f
|
||||
return decorator
|
||||
|
||||
|
|
@ -186,7 +205,7 @@ class Blueprint(_PackageBoundObject):
|
|||
blueprint. It's called before the view functions are called and
|
||||
can modify the url values provided.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.url_value_preprocessors
|
||||
self.record_once(lambda s: s.app.url_value_preprocessors
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -195,21 +214,21 @@ class Blueprint(_PackageBoundObject):
|
|||
with the endpoint and values and should update the values passed
|
||||
in place.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.url_default_functions
|
||||
self.record_once(lambda s: s.app.url_default_functions
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def app_url_value_preprocessor(self, f):
|
||||
"""Same as :meth:`url_value_preprocessor` but application wide.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.url_value_preprocessor
|
||||
self.record_once(lambda s: s.app.url_value_preprocessor
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def app_url_defaults(self, f):
|
||||
"""Same as :meth:`url_defaults` but application wide.
|
||||
"""
|
||||
self._record_once(lambda s: s.app.url_default_functions
|
||||
self.record_once(lambda s: s.app.url_default_functions
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
|
|
@ -227,7 +246,7 @@ class Blueprint(_PackageBoundObject):
|
|||
.. versionadded:: 0.7
|
||||
"""
|
||||
def decorator(f):
|
||||
self._record_once(lambda s: s.app._register_error_handler(
|
||||
self.record_once(lambda s: s.app._register_error_handler(
|
||||
self.name, code_or_exception, f))
|
||||
return f
|
||||
return decorator
|
||||
|
|
|
|||
|
|
@ -1,19 +1,10 @@
|
|||
from flask import Flask, Module
|
||||
from flask import Flask, Module, render_template
|
||||
|
||||
|
||||
mod = Module(__name__)
|
||||
mod2 = Module(__name__, 'testmod2')
|
||||
mod3 = Module(__name__, name='somemod', subdomain='meh')
|
||||
|
||||
app = Flask(__name__)
|
||||
app.register_module(mod)
|
||||
app.register_module(mod2)
|
||||
|
||||
|
||||
def handle_404(error):
|
||||
return 'Testing', 404
|
||||
app.error_handlers[404] = handle_404
|
||||
|
||||
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
|
|
@ -27,10 +18,20 @@ def index():
|
|||
|
||||
|
||||
@mod2.route('/')
|
||||
def index():
|
||||
def mod2_index():
|
||||
return render_template('testmod2/index.html')
|
||||
|
||||
|
||||
@mod2.route('/')
|
||||
def index():
|
||||
@mod3.route('/')
|
||||
def mod3_index():
|
||||
return render_template('something-else/index.html')
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.register_module(mod)
|
||||
app.register_module(mod2)
|
||||
|
||||
|
||||
def handle_404(error):
|
||||
return 'Testing', 404
|
||||
app.error_handlers[404] = handle_404
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue