From 727c701686c18c4a27ca523eb0c5862d497be24e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 9 Apr 2010 13:40:05 +0200 Subject: [PATCH] And finished documentation for most parts. --- docs/api.rst | 2 + docs/index.rst | 10 +- docs/patterns.rst | 166 ++++++++++++++++++++++++++++++++++ docs/quickstart.rst | 23 +++++ examples/minitwit/minitwit.py | 2 +- 5 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 docs/patterns.rst diff --git a/docs/api.rst b/docs/api.rst index 6c763393..eefdf71c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,3 +1,5 @@ +.. _api: + API === diff --git a/docs/index.rst b/docs/index.rst index 5e5d8622..ddb41da5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,13 +1,17 @@ Welcome to Flask ================ -Welcome to Flask's documentation. - -Contents: +Welcome to Flask's documentation. This documentation is devided into +different parts. I would suggest to get started with the +:ref:`installation` and then heading over to the :ref:`quickstart`. If +you want to dive into all the internal parts of Flask, check out the +:ref:`api` documentation. Common patterns are described in the +:ref:`patterns` section. .. toctree:: :maxdepth: 2 installation quickstart + patterns api diff --git a/docs/patterns.rst b/docs/patterns.rst new file mode 100644 index 00000000..5047ea73 --- /dev/null +++ b/docs/patterns.rst @@ -0,0 +1,166 @@ +.. _patterns: + +Patterns in Flask +================= + +Certain things are common enough that the changes are high you will find +them in most web applications. For example quite a lot of applications +are using relational databases and user authentication. In that case, +changes are they will open a database connection at the beginning of the +request and get the information of the currently logged in user. At the +end of the request, the database connection is closed again. + +In Flask you can implement such things with the +:meth:`~flask.Flask.request_init` and +:meth:`~flask.Flask.request_shutdown` decorators in combination with the +special :class:`~flask.g` object. + + +Using SQLite 3 with Flask +------------------------- + +So here a simple example how you can use SQLite 3 with Flask:: + + import sqlite3 + from flask import g + + DATABASE = '/path/to/database.db' + + def connect_db(): + return sqlite3.connect(DATABASE) + + @app.request_init + def before_request(): + g.db = connect_db() + + @app.request_shutdown + def after_request(response): + g.db.close() + return response + +Easy Querying +````````````` + +Now in each request handling function you can access `g.db` to get the +current open database connection. To simplify working with SQLite a +helper function can be useful:: + + def query_db(query, args=(), one=False): + cur = g.db.execute(query, args) + rv = [dict((cur.description[idx][0], value) + for idx, value in enumerate(row)) for row in cur.fetchall()] + return (rv[0] if rv else None) if one else rv + +This handy little function makes working with the database much more +pleasant than it is by just using the raw cursor and connection objects. + +Here is how you can use it:: + + for user in query_db('select * from users'): + print user['username'], 'has the id', user['user_id'] + +Or if you just want a single result:: + + user = query_db('select * from users where username = ?', + [the_username], one=True) + if user is None: + print 'No such user' + else: + print the_username, 'has the id', user['user_id'] + +To pass variable parts to the SQL statement, use a question mark in the +statement and pass in the arguments as a list. Never directly add them to +the SQL statement with string formattings because this makes it possible +to attack the application using `SQL Injections +`_. + +Initial Schemas +``````````````` + +Relational databases need schemas, so applications often ship a +`schema.sql` file that creates the database. It's a good idea to provide +a function that creates the database bases on that schema. This function +can do that for you:: + + from contextlib import closing + + def init_db(): + with closing(connect_db()) as db: + with app.open_resource('schema.sql') as f: + db.cursor().executescript(f.read()) + db.commit() + +You can then create such a database from the python shell: + +>>> from yourapplication import init_db +>>> init_db() + +.. _template-inheritance: + +Template Inheritance +-------------------- + +The most powerful part of Jinja is template inheritance. Template inheritance +allows you to build a base "skeleton" template that contains all the common +elements of your site and defines **blocks** that child templates can override. + +Sounds complicated but is very basic. It's easiest to understand it by starting +with an example. + + +Base Template +````````````` + +This template, which we'll call ``layout.html``, defines a simple HTML skeleton +document that you might use for a simple two-column page. It's the job of +"child" templates to fill the empty blocks with content: + +.. sourcecode:: html+jinja + + + + + {% block head %} + + {% block title %}{% endblock %} - My Webpage + {% endblock %} + + +
{% block content %}{% endblock %}
+ + + +In this example, the ``{% block %}`` tags define four blocks that child templates +can fill in. All the `block` tag does is to tell the template engine that a +child template may override those portions of the template. + +Child Template +`````````````` + +A child template might look like this: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block title %}Index{% endblock %} + {% block head %} + {{ super() }} + + {% endblock %} + {% block content %} +

Index

+

+ Welcome on my awesome homepage. + {% endblock %} + +The ``{% extends %}`` tag is the key here. It tells the template engine that +this template "extends" another template. When the template system evaluates +this template, first it locates the parent. The extends tag must be the +first tag in the template. To render the contents of a block defined in +the parent template, use ``{{ super() }}``. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index e95e467a..64ef0368 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -1,3 +1,5 @@ +.. _quickstart: + Quickstart ========== @@ -148,6 +150,22 @@ don't have to deal with that. It will also make sure that ``HEAD`` requests are handled like the RFC demands, so you can completely ignore that part of the HTTP specification. +Static Files +------------ + +Dynamic web applications need static files as well. That's usually where +the CSS and JavaScript files are coming from. Ideally your web server is +configured to serve them for you, but during development Flask can do that +as well. Just create a folder called `static` in your package or next to +your module and it will be available at `/static` on the application. + +To generate URLs to that part of the URL, use the special ``'static'`` URL +name:: + + url_for('static', filename='style.css') + +The file has to be stored on the filesystem as ``static/style.css``. + Rendering Templates ------------------- @@ -205,6 +223,11 @@ Inside templates you also have access to the :class:`~flask.request`, :class:`~flask.session` and :class:`~flask.g` objects as well as the :func:`~flask.get_flashed_messages` function. +Templates are especially useful if inheritance is used. If you want to +know how that works, head over to the :ref:`template-inheritance` pattern +documentation. Basically template inheritance makes it possible to keep +certain elements on each page (like header, navigation and footer). + Automatic escaping is enabled, so if name contains HTML it will be escaped automatically. If you can trust a variable and you know that it will be safe HTML (because for example it came from a module that converts wiki diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index ebb304a7..06168746 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -65,7 +65,7 @@ def before_request(): """Make sure we are connected to the database each request and look up the current user so that we know he's there. """ - g.db = sqlite3.connect(DATABASE) + g.db = connect_db() if 'user_id' in session: g.user = query_db('select * from user where user_id = ?', [session['user_id']], one=True)