diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 0bfd7df7..e95e467a 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -93,6 +93,40 @@ The following converters exist: `path` like the default but also accepts slashes =========== =========================================== +URL Building +```````````` + +If it can match URLs, can it also generate them? Of course you can. To +build a URL to a specific function you can use the :func:`~flask.url_for` +function. It accepts the name of the function as first argument and a +number of keyword arguments, each corresponding to the variable part of +the URL rule. Here some examples: + +>>> from flask import Flask, url_for +>>> app = Flask(__name__) +>>> @app.route('/') +... def index(): pass +... +>>> @app.route('/login') +... def login(): pass +... +>>> @app.route('/user/') +... def profile(username): pass +... +>>> with app.test_request_context(): +... print url_for('index') +... print url_for('login') +... print url_for('profile', username='John Doe') +... +/ +/login +/user/John%20Doe + +(This also uses the :meth:`~flask.Flask.test_request_context` method +explained below. It basically tells flask to think we are handling a +request even though we are not, we are in an interactive Python shell. +Have a look at the explanation below. :ref:`context-locals`). + HTTP Methods ```````````` @@ -114,6 +148,80 @@ 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. +Rendering Templates +------------------- + +Generating HTML from within Python is not fun, and actually pretty +cumbersome because you have to do the HTML escaping on your own to keep +the application secure. Because of that Flask configures the `Jinja2 +`_ template engine for you automatically. + +To render a template you can use the :func:`~flask.render_template` +method. All you have to do is to provide the name of the template and the +variables you want to pass to the template engine as keyword arguments. +Here a simple example of how to render a template:: + + from flask import render_template + + @app.route('/hello/') + @app.route('/hello/') + def hello(name=None): + return render_template('hello.html', name=name) + +Flask will look for templates in the `templates` folder. So if your +application is a module, that folder is next to that module, if it's a +pacakge it's actually inside your package: + +**Case 1**: a module:: + + /application.py + /templates + /hello.html + +**Case 2**: a package:: + + /application + /__init__.py + /templates + /hello.html + +For templates you can use the full power of Jinja2 templates. Head over +to the `Jinja2 Template Documentation +`_ for more information. + +Here an example template: + +.. sourcecode:: html+jinja + + + Hello from Flask + {% if name %} +

Hello {{ name }}!

+ {% else %} +

Hello World!

+ {% endif %} + +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. + +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 +markup to HTML) you can mark it as safe by using the +:class:`~jinja2.Markup` class or by using the ``|safe`` filter in the +template. Head over to the Jinja 2 documentation for more examples. + +Here a basic introduction in how the :class:`~jinja2.Markup` class works: + +>>> from flask import Markup +>>> Markup('Hello %s!') % 'hacker' +Markup(u'Hello <blink>hacker</blink>!') +>>> Markup.escape('hacker') +Markup(u'<blink>hacker</blink>') +>>> Markup('Marked up » HTML').striptags() +u'Marked up \xbb HTML' + Accessing Request Data ---------------------- @@ -124,6 +232,9 @@ the server. In Flask this information is provided by the global you might be wondering how that object can be global and how Flask manages to still be threadsafe. The answer are context locals: + +.. _context-locals: + Context Locals `````````````` @@ -271,6 +382,39 @@ transmits. If you want to use sessions, do not use the cookies directly but instead use the :ref:`sessions` in Flask that add some security on top of cookies for you. + +Redirects and Errors +-------------------- + +To redirect a user to somewhere else you can use the +:func:`~flask.redirect` function, to abort a request early with an error +code the :func:`~flask.abort` function. Here an example how this works:: + + from flask import abort, redirect, url_for + + @app.route('/') + def index(): + return redirect(url_for('login')) + + @app.route('/login') + def login(): + abort(401) + this_is_never_executed() + +This is a rather pointless example because a user will be redirected from +the index to a page he cannot access (401 means access denied) but it +shows how that works. + +By default a black and white error page is shown for each error code. If +you want to customize the error page, you can use the +:meth:`~flask.Flask.errorhandler` decorator:: + + from flask import render_template + + @app.errorhandler(404) + def page_not_found(error): + return render_template('page_not_found.html') + .. _sessions: Sessions @@ -310,3 +454,83 @@ sessions work:: def logout(): # remove the username from the session if its there session.pop('username', None) + +Message Flashing +---------------- + +Good applications and user interfaces are all about feedback. If the user +does not get enough feedback he will probably end up hating the +application. Flask provides a really simple way to give feedback to a +user with the flashing system. The flashing system basically makes it +possible to record a message at the end of a request and access it next +request and only next request. This is usually combined with a layout +template that does this. + +So here a full example:: + + from flask import flash, redirect, url_for, render_template + + @app.route('/') + def index(): + return render_template('index.html') + + @app.route('/login', methods=['GET', 'POST']) + def login(): + error = None + if request.method == 'POST': + if request.form['username'] != 'admin' or \ + request.form['password'] != 'secret': + error = 'Invalid credentials' + else: + flash('You were sucessfully logged in') + return redirect(url_for('index')) + return render_template('login.html', error=error) + +And here the ``layout.html`` template which does the magic: + +.. sourcecode:: html+jinja + + + My Application + {% with messages = get_flashed_messages() %} + {% if messages %} +
    + {% for message in messages %} +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + {% block body %}{% endblock %} + +And here the index.html template: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block body %} +

Overview

+

Do you want to log in? + {% endblock %} + +And of course the login template: + +.. sourcecode:: html+jinja + + {% extends "layout.html" %} + {% block body %} +

Login

+ {% if error %} +

Error: {{ error }} + {% endif %} +

+
+
Username: +
+
Password: +
+
+

+

+ {% endblock %}