mirror of https://github.com/mitsuhiko/flask.git
Armin Ronacher
15 years ago
15 changed files with 147 additions and 3 deletions
@ -1 +1 @@ |
|||||||
Subproject commit 09eeca526b2b5675cc29f45917f5d0f795395035 |
Subproject commit 91eee537e91594f752224a5847719f6d4fb38c2d |
@ -0,0 +1,104 @@ |
|||||||
|
Lazily Loading Views |
||||||
|
==================== |
||||||
|
|
||||||
|
Flask is usually used with the decorators. Decorators are simple and you |
||||||
|
have the URL right next to the function that is called for that specific |
||||||
|
URL. However there is a downside to this approach: it means all your code |
||||||
|
that uses decorators has to be imported upfront or Flask will never |
||||||
|
actually find your function. |
||||||
|
|
||||||
|
This can be a problem if your application has to import quick. It might |
||||||
|
have to do that on systems like Google's AppEngine or other systems. So |
||||||
|
if you suddenly notice that your application outgrows this approach you |
||||||
|
can fall back to a centralized URL mapping. |
||||||
|
|
||||||
|
The system that enables having a central URL map is the |
||||||
|
:meth:`~flask.Flask.add_url_rule` function. Instead of using decorators, |
||||||
|
you have a file that sets up the application with all URLs. |
||||||
|
|
||||||
|
Converting to Centralized URL Map |
||||||
|
--------------------------------- |
||||||
|
|
||||||
|
Imagine the current application looks somewhat like this:: |
||||||
|
|
||||||
|
from flask import Flask |
||||||
|
app = Flask(__name__) |
||||||
|
|
||||||
|
@app.route('/') |
||||||
|
def index(): |
||||||
|
pass |
||||||
|
|
||||||
|
@app.route('/user/<username>') |
||||||
|
def user(username): |
||||||
|
pass |
||||||
|
|
||||||
|
Then the centralized approach you would have one file with the views |
||||||
|
(`views.py`) but without any decorator:: |
||||||
|
|
||||||
|
def index(): |
||||||
|
pass |
||||||
|
|
||||||
|
def user(username): |
||||||
|
pass |
||||||
|
|
||||||
|
And then a file that sets up an application which maps the functions to |
||||||
|
URLs:: |
||||||
|
|
||||||
|
from flask import Flask |
||||||
|
from yourapplication import views |
||||||
|
app = Flask(__name__) |
||||||
|
app.add_url_rule('/', view_func=views.index) |
||||||
|
app.add_url_rule('/user/<username>', view_func=views.user) |
||||||
|
|
||||||
|
Loading Late |
||||||
|
------------ |
||||||
|
|
||||||
|
So far we only split up the views and the routing, but the module is still |
||||||
|
loaded upfront. The trick to actually load the view function as needed. |
||||||
|
This can be accomplished with a helper class that behaves just like a |
||||||
|
function but internally imports the real function on first use:: |
||||||
|
|
||||||
|
from werkzeug import import_string, cached_property |
||||||
|
|
||||||
|
class LazyView(object): |
||||||
|
|
||||||
|
def __init__(self, import_name): |
||||||
|
self.__module__, self.__name__ = import_name.rsplit('.', 1) |
||||||
|
self.import_name = import_name |
||||||
|
|
||||||
|
@cached_property |
||||||
|
def view(self): |
||||||
|
return import_string(self.import_name) |
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs): |
||||||
|
return self.view(*args, **kwargs) |
||||||
|
|
||||||
|
What's important here is is that `__module__` and `__name__` are properly |
||||||
|
set. This is used by Flask internally to figure out how to do name the |
||||||
|
URL rules in case you don't provide a name for the rule yourself. |
||||||
|
|
||||||
|
Then you can define your central place to combine the views like this:: |
||||||
|
|
||||||
|
from flask import Flask |
||||||
|
from yourapplication.helpers import LazyView |
||||||
|
app = Flask(__name__) |
||||||
|
app.add_url_rule('/', |
||||||
|
view_func=LazyView('yourapplication.views.index')) |
||||||
|
app.add_url_rule('/user/<username>', |
||||||
|
view_func=LazyView('yourapplication.views.user')) |
||||||
|
|
||||||
|
You can further optimize this in terms of amount of keystrokes needed to |
||||||
|
write this by having a function that calls into |
||||||
|
:meth:`~flask.Flask.add_url_rule` by prefixing a string with the project |
||||||
|
name and a dot, and by wrapping `view_func` in a `LazyView` as needed:: |
||||||
|
|
||||||
|
def url(url_rule, import_name, **options): |
||||||
|
view = LazyView('yourapplication.' + import_name) |
||||||
|
app.add_url_rule(url_rule, view_func=view, **options) |
||||||
|
|
||||||
|
url('/', 'views.index') |
||||||
|
url('/user/<username>', 'views.user') |
||||||
|
|
||||||
|
One thing to keep in mind is that before and after request handlers have |
||||||
|
to be in a file that is imported upfront to work propery on the first |
||||||
|
request. The same goes for any kind of remaining decorator. |
Loading…
Reference in new issue