forked from orbit-oss/flask
Added lazyloading pattern and explicit chapter links in tutorial.
This fixes #49.
This commit is contained in:
parent
6c095deda5
commit
9d19b77acf
15 changed files with 147 additions and 3 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 09eeca526b2b5675cc29f45917f5d0f795395035
|
||||
Subproject commit 91eee537e91594f752224a5847719f6d4fb38c2d
|
||||
|
|
@ -140,7 +140,7 @@ html_sidebars = {
|
|||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
html_use_modindex = False
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
|
@ -152,7 +152,7 @@ html_sidebars = {
|
|||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
html_show_sphinx = False
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
|
|
|||
|
|
@ -28,3 +28,4 @@ Snippet Archives <http://flask.pocoo.org/snippets/>`_.
|
|||
flashing
|
||||
jquery
|
||||
errorpages
|
||||
lazyloading
|
||||
|
|
|
|||
104
docs/patterns/lazyloading.rst
Normal file
104
docs/patterns/lazyloading.rst
Normal file
|
|
@ -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.
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-css:
|
||||
|
||||
Step 7: Adding Style
|
||||
====================
|
||||
|
||||
|
|
@ -25,3 +27,5 @@ folder we created before:
|
|||
.flash { background: #CEE5F5; padding: 0.5em;
|
||||
border: 1px solid #AACBE2; }
|
||||
.error { background: #F0D6D6; padding: 0.5em; }
|
||||
|
||||
Continue with :ref:`tutorial-testing`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-dbcon:
|
||||
|
||||
Step 4: Request Database Connections
|
||||
------------------------------------
|
||||
|
||||
|
|
@ -31,3 +33,5 @@ request only and is available from within each function. Never store such
|
|||
things on other objects because this would not work with threaded
|
||||
environments. That special :data:`~flask.g` object does some magic behind
|
||||
the scenes to ensure it does the right thing.
|
||||
|
||||
Continue to :ref:`tutorial-views`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-dbinit:
|
||||
|
||||
Step 3: Creating The Database
|
||||
=============================
|
||||
|
||||
|
|
@ -61,3 +63,5 @@ importing and calling that function::
|
|||
If you get an exception later that a table cannot be found check that
|
||||
you did call the `init_db` function and that your table names are
|
||||
correct (singular vs. plural for example).
|
||||
|
||||
Continue with :ref:`tutorial-dbcon`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-folders:
|
||||
|
||||
Step 0: Creating The Folders
|
||||
============================
|
||||
|
||||
|
|
@ -16,4 +18,6 @@ This is the place where css and javascript files go. Inside the
|
|||
`templates` folder Flask will look for `Jinja2`_ templates. Drop all the
|
||||
templates there.
|
||||
|
||||
Continue with :ref:`tutorial-schema`.
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/2/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-introduction:
|
||||
|
||||
Introducing Flaskr
|
||||
==================
|
||||
|
||||
|
|
@ -26,4 +28,6 @@ Here a screenshot from the final application:
|
|||
:class: screenshot
|
||||
:alt: screenshot of the final application
|
||||
|
||||
Continue with :ref:`tutorial-folders`.
|
||||
|
||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-schema:
|
||||
|
||||
Step 1: Database Schema
|
||||
=======================
|
||||
|
||||
|
|
@ -19,3 +21,5 @@ This schema consists of a single table called `entries` and each row in
|
|||
this table has an `id`, a `title` and a `text`. The `id` is an
|
||||
automatically incrementing integer and a primary key, the other two are
|
||||
strings that must not be null.
|
||||
|
||||
Continue with :ref:`tutorial-setup`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-setup:
|
||||
|
||||
Step 2: Application Setup Code
|
||||
==============================
|
||||
|
||||
|
|
@ -62,3 +64,5 @@ focus on that a little later. First we should get the database working.
|
|||
Want your server to be publically available? Check out the
|
||||
:ref:`externally visible server <public-server>` section for more
|
||||
information.
|
||||
|
||||
Continue with :ref:`tutorial-dbinit`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-templates:
|
||||
|
||||
Step 6: The Templates
|
||||
=====================
|
||||
|
||||
|
|
@ -105,3 +107,5 @@ the user to login:
|
|||
</dl>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
Continue with :ref:`tutorial-css`.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-testing:
|
||||
|
||||
Bonus: Testing the Application
|
||||
===============================
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _tutorial-views:
|
||||
|
||||
Step 5: The View Functions
|
||||
==========================
|
||||
|
||||
|
|
@ -85,3 +87,5 @@ that case if the user was logged in.
|
|||
session.pop('logged_in', None)
|
||||
flash('You were logged out')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
||||
Continue with :ref:`tutorial-templates`.
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ def user_timeline(username):
|
|||
follower.who_id = ? and follower.whom_id = ?''',
|
||||
[session['user_id'], profile_user['user_id']],
|
||||
one=True) is not None
|
||||
broken_just_for_djangocon
|
||||
return render_template('timeline.html', messages=query_db('''
|
||||
select message.*, user.* from message, user where
|
||||
user.user_id = message.author_id and user.user_id = ?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue