`::
+
+ from flask import Flask, render_template
+
+ def page_not_found(e):
+ return render_template('404.html'), 404
+
+ def create_app(config_filename):
+ app = Flask(__name__)
+ app.register_error_handler(404, page_not_found)
+ return app
+
An example template might be this:
.. sourcecode:: html+jinja
- {% extends "layout.html" %}
- {% block title %}Page Not Found{% endblock %}
- {% block body %}
- Page Not Found
- What you were looking for is just not there.
-
go somewhere nice
- {% endblock %}
-
+ {% extends "layout.html" %}
+ {% block title %}Page Not Found{% endblock %}
+ {% block body %}
+
Page Not Found
+ What you were looking for is just not there.
+
go somewhere nice
+ {% endblock %}
diff --git a/docs/patterns/favicon.rst b/docs/patterns/favicon.rst
index acdee24b..21ea767f 100644
--- a/docs/patterns/favicon.rst
+++ b/docs/patterns/favicon.rst
@@ -49,5 +49,5 @@ web server's documentation.
See also
--------
-* The `Favicon `_ article on
+* The `Favicon `_ article on
Wikipedia
diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst
index 8ab8c033..1c4b0d36 100644
--- a/docs/patterns/fileuploads.rst
+++ b/docs/patterns/fileuploads.rst
@@ -21,7 +21,7 @@ specific upload folder and displays a file to the user. Let's look at the
bootstrapping code for our application::
import os
- from flask import Flask, request, redirect, url_for
+ from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads'
@@ -58,7 +58,7 @@ the file and redirects the user to the URL for the uploaded file::
return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
- # submit a empty part without filename
+ # submit an empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(request.url)
@@ -72,8 +72,8 @@ the file and redirects the user to the URL for the uploaded file::
Upload new File
Upload new File
'''
@@ -181,4 +181,4 @@ applications dealing with uploads, there is also a Flask extension called
blacklisting of extensions and more.
.. _jQuery: https://jquery.com/
-.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/
+.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/
diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst
index af51717d..cc149839 100644
--- a/docs/patterns/packages.rst
+++ b/docs/patterns/packages.rst
@@ -8,15 +8,19 @@ module. That is quite simple. Imagine a small application looks like
this::
/yourapplication
- /yourapplication.py
+ yourapplication.py
/static
- /style.css
+ style.css
/templates
layout.html
index.html
login.html
...
+If you find yourself stuck on something, feel free
+to take a look at the source code for this example.
+You'll find `the full src for this example here`_.
+
Simple Packages
---------------
@@ -29,9 +33,9 @@ You should then end up with something like that::
/yourapplication
/yourapplication
- /__init__.py
+ __init__.py
/static
- /style.css
+ style.css
/templates
layout.html
index.html
@@ -41,11 +45,36 @@ You should then end up with something like that::
But how do you run your application now? The naive ``python
yourapplication/__init__.py`` will not work. Let's just say that Python
does not want modules in packages to be the startup file. But that is not
-a big problem, just add a new file called :file:`runserver.py` next to the inner
+a big problem, just add a new file called :file:`setup.py` next to the inner
:file:`yourapplication` folder with the following contents::
- from yourapplication import app
- app.run(debug=True)
+ from setuptools import setup
+
+ setup(
+ name='yourapplication',
+ packages=['yourapplication'],
+ include_package_data=True,
+ install_requires=[
+ 'flask',
+ ],
+ )
+
+In order to run the application you need to export an environment variable
+that tells Flask where to find the application instance::
+
+ export FLASK_APP=yourapplication
+
+If you are outside of the project directory make sure to provide the exact
+path to your application directory. Similarly you can turn on "debug
+mode" with this environment variable::
+
+ export FLASK_DEBUG=true
+
+In order to install and run the application you need to issue the following
+commands::
+
+ pip install -e .
+ flask run
What did we gain from this? Now we can restructure the application a bit
into multiple modules. The only thing you have to remember is the
@@ -77,12 +106,12 @@ And this is what :file:`views.py` would look like::
You should then end up with something like that::
/yourapplication
- /runserver.py
+ setup.py
/yourapplication
- /__init__.py
- /views.py
+ __init__.py
+ views.py
/static
- /style.css
+ style.css
/templates
layout.html
index.html
@@ -105,6 +134,7 @@ You should then end up with something like that::
.. _working-with-modules:
+.. _the full src for this example here: https://github.com/pallets/flask/tree/master/examples/patterns/largerapp
Working with Blueprints
-----------------------
diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst
index 40e048e0..8785a6e2 100644
--- a/docs/patterns/sqlalchemy.rst
+++ b/docs/patterns/sqlalchemy.rst
@@ -22,7 +22,7 @@ if you want to get started quickly.
You can download `Flask-SQLAlchemy`_ from `PyPI
`_.
-.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/
+.. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/
Declarative
@@ -108,9 +108,9 @@ Querying is simple as well:
>>> User.query.filter(User.name == 'admin').first()
-.. _SQLAlchemy: http://www.sqlalchemy.org/
+.. _SQLAlchemy: https://www.sqlalchemy.org/
.. _declarative:
- http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
+ https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
Manual Object Relational Mapping
--------------------------------
@@ -135,7 +135,7 @@ Here is an example :file:`database.py` module for your application::
def init_db():
metadata.create_all(bind=engine)
-As for the declarative approach you need to close the session after
+As in the declarative approach, you need to close the session after
each request or application context shutdown. Put this into your
application module::
@@ -215,4 +215,4 @@ You can also pass strings of SQL statements to the
(1, u'admin', u'admin@localhost')
For more information about SQLAlchemy, head over to the
-`website `_.
+`website `_.
diff --git a/docs/patterns/sqlite3.rst b/docs/patterns/sqlite3.rst
index 66a7c4c4..15f38ea7 100644
--- a/docs/patterns/sqlite3.rst
+++ b/docs/patterns/sqlite3.rst
@@ -3,8 +3,8 @@
Using SQLite 3 with Flask
=========================
-In Flask you can easily implement the opening of database connections on
-demand and closing them when the context dies (usually at the end of the
+In Flask you can easily implement the opening of database connections on
+demand and closing them when the context dies (usually at the end of the
request).
Here is a simple example of how you can use SQLite 3 with Flask::
@@ -71,7 +71,7 @@ Now in each request handling function you can access `g.db` to get the
current open database connection. To simplify working with SQLite, a
row factory function is useful. It is executed for every result returned
from the database to convert the result. For instance, in order to get
-dictionaries instead of tuples, this could be inserted into the ``get_db``
+dictionaries instead of tuples, this could be inserted into the ``get_db``
function we created above::
def make_dicts(cursor, row):
@@ -102,15 +102,15 @@ This would use Row objects rather than dicts to return the results of queries. T
Additionally, it is a good idea to provide a query function that combines
getting the cursor, executing and fetching the results::
-
+
def query_db(query, args=(), one=False):
cur = get_db().execute(query, args)
rv = cur.fetchall()
cur.close()
return (rv[0] if rv else None) if one else rv
-This handy little function, in combination with a row factory, makes
-working with the database much more pleasant than it is by just using the
+This handy little function, in combination with a row factory, 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::
@@ -131,7 +131,7 @@ 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 formatting because this makes it possible
to attack the application using `SQL Injections
-`_.
+`_.
Initial Schemas
---------------
diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst
index 2649cad6..0e53de17 100644
--- a/docs/patterns/wtforms.rst
+++ b/docs/patterns/wtforms.rst
@@ -19,7 +19,7 @@ forms.
fun. You can get it from `PyPI
`_.
-.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/
+.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/
The Forms
---------
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 433e4e08..d56fa8e2 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -50,7 +50,14 @@ to tell your terminal the application to work with by exporting the
$ flask run
* Running on http://127.0.0.1:5000/
-If you are on Windows you need to use ``set`` instead of ``export``.
+If you are on Windows, the environment variable syntax depends on command line
+interpreter. On Command Prompt::
+
+ C:\path\to\app>set FLASK_APP=hello.py
+
+And on PowerShell::
+
+ PS C:\path\to\app> $env:FLASK_APP = "hello.py"
Alternatively you can use :command:`python -m flask`::
@@ -102,10 +109,10 @@ docs to see the alternative method for running a server.
Invalid Import Name
```````````````````
-The ``-a`` argument to :command:`flask` is the name of the module to
-import. In case that module is incorrectly named you will get an import
-error upon start (or if debug is enabled when you navigate to the
-application). It will tell you what it tried to import and why it failed.
+The ``FLASK_APP`` environment variable is the name of the module to import at
+:command:`flask run`. In case that module is incorrectly named you will get an
+import error upon start (or if debug is enabled when you navigate to the
+application). It will tell you what it tried to import and why it failed.
The most common reason is a typo or because you did not actually create an
``app`` object.
@@ -153,20 +160,22 @@ Screenshot of the debugger in action:
:class: screenshot
:alt: screenshot of debugger in action
+More information on using the debugger can be found in the `Werkzeug
+documentation`_.
+
+.. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/debug/#using-the-debugger
+
Have another debugger in mind? See :ref:`working-with-debuggers`.
Routing
-------
-Modern web applications have beautiful URLs. This helps people remember
-the URLs, which is especially handy for applications that are used from
-mobile devices with slower network connections. If the user can directly
-go to the desired page without having to hit the index page it is more
-likely they will like the page and come back next time.
+Modern web applications use meaningful URLs to help users. Users are more
+likely to like a page and come back if the page uses a meaningful URL they can
+remember and use to directly visit a page.
-As you have seen above, the :meth:`~flask.Flask.route` decorator is used to
-bind a function to a URL. Here are some basic examples::
+Use the :meth:`~flask.Flask.route` decorator to bind a function to a URL. ::
@app.route('/')
def index():
@@ -176,16 +185,16 @@ bind a function to a URL. Here are some basic examples::
def hello():
return 'Hello, World'
-But there is more to it! You can make certain parts of the URL dynamic and
-attach multiple rules to a function.
+You can do more! You can make parts of the URL dynamic and attach multiple
+rules to a function.
Variable Rules
``````````````
-To add variable parts to a URL you can mark these special sections as
-````. Such a part is then passed as a keyword argument to your
-function. Optionally a converter can be used by specifying a rule with
-````. Here are some nice examples::
+You can add variable sections to a URL by marking sections with
+````. Your function then receives the ````
+as a keyword argument. Optionally, you can use a converter to specify the type
+of the argument like ````. ::
@app.route('/user/')
def show_user_profile(username):
@@ -197,111 +206,111 @@ function. Optionally a converter can be used by specifying a rule with
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
-The following converters exist:
-
-=========== ===============================================
-`string` accepts any text without a slash (the default)
-`int` accepts integers
-`float` like ``int`` but for floating point values
-`path` like the default but also accepts slashes
-`any` matches one of the items provided
-`uuid` accepts UUID strings
-=========== ===============================================
+ @app.route('/path/')
+ def show_subpath(subpath):
+ # show the subpath after /path/
+ return 'Subpath %s' % subpath
-.. admonition:: Unique URLs / Redirection Behavior
+Converter types:
- Flask's URL rules are based on Werkzeug's routing module. The idea
- behind that module is to ensure beautiful and unique URLs based on
- precedents laid down by Apache and earlier HTTP servers.
+========== ==========================================
+``string`` (default) accepts any text without a slash
+``int`` accepts positive integers
+``float`` accepts positive floating point values
+``path`` like ``string`` but also accepts slashes
+``uuid`` accepts UUID strings
+========== ==========================================
- Take these two rules::
+Unique URLs / Redirection Behavior
+``````````````````````````````````
- @app.route('/projects/')
- def projects():
- return 'The project page'
+Take these two rules::
- @app.route('/about')
- def about():
- return 'The about page'
+ @app.route('/projects/')
+ def projects():
+ return 'The project page'
- Though they look rather similar, they differ in their use of the trailing
- slash in the URL *definition*. In the first case, the canonical URL for the
- ``projects`` endpoint has a trailing slash. In that sense, it is similar to
- a folder on a filesystem. Accessing it without a trailing slash will cause
- Flask to redirect to the canonical URL with the trailing slash.
+ @app.route('/about')
+ def about():
+ return 'The about page'
- In the second case, however, the URL is defined without a trailing slash,
- rather like the pathname of a file on UNIX-like systems. Accessing the URL
- with a trailing slash will produce a 404 "Not Found" error.
+Though they look similar, they differ in their use of the trailing slash in
+the URL. In the first case, the canonical URL for the ``projects`` endpoint
+uses a trailing slash. It's similar to a folder in a file system; if you
+access the URL without a trailing slash, Flask redirects you to the
+canonical URL with the trailing slash.
- This behavior allows relative URLs to continue working even if the trailing
- slash is omitted, consistent with how Apache and other servers work. Also,
- the URLs will stay unique, which helps search engines avoid indexing the
- same page twice.
+In the second case, however, the URL definition lacks a trailing slash,
+like the pathname of a file on UNIX-like systems. Accessing the URL with a
+trailing slash produces a 404 “Not Found” error.
+This behavior allows relative URLs to continue working even if the trailing
+slash is omitted, consistent with how Apache and other servers work. Also,
+the URLs will stay unique, which helps search engines avoid indexing the
+same page twice.
.. _url-building:
URL Building
````````````
-If it can match URLs, can Flask also generate them? Of course it 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.
-Unknown variable parts are appended to the URL as query parameters. Here are
-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('login', next='/')
- ... print url_for('profile', username='John Doe')
- ...
- /
- /login
- /login?next=/
- /user/John%20Doe
-
-(This also uses the :meth:`~flask.Flask.test_request_context` method, explained
-below. It tells Flask to behave as though it is handling a request, even
-though we are interacting with it through a Python shell. Have a look at the
-explanation below. :ref:`context-locals`).
+To build a URL to a specific function, use the :func:`~flask.url_for` function.
+It accepts the name of the function as its first argument and any number of
+keyword arguments, each corresponding to a variable part of the URL rule.
+Unknown variable parts are appended to the URL as query parameters.
Why would you want to build URLs using the URL reversing function
:func:`~flask.url_for` instead of hard-coding them into your templates?
-There are three good reasons for this:
-1. Reversing is often more descriptive than hard-coding the URLs. More
- importantly, it allows you to change URLs in one go, without having to
- remember to change URLs all over the place.
-2. URL building will handle escaping of special characters and Unicode
- data transparently for you, so you don't have to deal with them.
-3. If your application is placed outside the URL root - say, in
- ``/myapplication`` instead of ``/`` - :func:`~flask.url_for` will handle
- that properly for you.
+1. Reversing is often more descriptive than hard-coding the URLs.
+2. You can change your URLs in one go instead of needing to remember to
+ manually change hard-coded URLs.
+3. URL building handles escaping of special characters and Unicode data
+ transparently.
+4. If your application is placed outside the URL root, for example, in
+ ``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly
+ handles that for you.
+
+For example, here we use the :meth:`~flask.Flask.test_request_context` method
+to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context`
+tells Flask to behave as though it's handling a request even while we use a
+Python shell. See :ref:`context-locals`. ::
+ from flask import Flask, url_for
+
+ app = Flask(__name__)
+
+ @app.route('/')
+ def index():
+ return 'index'
+
+ @app.route('/login')
+ def login():
+ return 'login'
+
+ @app.route('/user/')
+ def profile(username):
+ return '{}'s profile'.format(username)
+
+ with app.test_request_context():
+ print(url_for('index'))
+ print(url_for('login'))
+ print(url_for('login', next='/'))
+ print(url_for('profile', username='John Doe'))
+
+ /
+ /login
+ /login?next=/
+ /user/John%20Doe
HTTP Methods
````````````
-HTTP (the protocol web applications are speaking) knows different methods for
-accessing URLs. By default, a route only answers to ``GET`` requests, but that
-can be changed by providing the ``methods`` argument to the
-:meth:`~flask.Flask.route` decorator. Here are some examples::
-
- from flask import request
+Web applications use different HTTP methods when accessing URLs. You should
+familiarize yourself with the HTTP methods as you work with Flask. By default,
+a route only answers to ``GET`` requests. You can use the ``methods`` argument
+of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
+::
@app.route('/login', methods=['GET', 'POST'])
def login():
@@ -310,64 +319,11 @@ can be changed by providing the ``methods`` argument to the
else:
show_the_login_form()
-If ``GET`` is present, ``HEAD`` will be added automatically for you. You
-don't have to deal with that. It will also make sure that ``HEAD`` requests
-are handled as the `HTTP RFC`_ (the document describing the HTTP
-protocol) demands, so you can completely ignore that part of the HTTP
-specification. Likewise, as of Flask 0.6, ``OPTIONS`` is implemented for you
-automatically as well.
-
-You have no idea what an HTTP method is? Worry not, here is a quick
-introduction to HTTP methods and why they matter:
-
-The HTTP method (also often called "the verb") tells the server what the
-client wants to *do* with the requested page. The following methods are
-very common:
-
-``GET``
- The browser tells the server to just *get* the information stored on
- that page and send it. This is probably the most common method.
-
-``HEAD``
- The browser tells the server to get the information, but it is only
- interested in the *headers*, not the content of the page. An
- application is supposed to handle that as if a ``GET`` request was
- received but to not deliver the actual content. In Flask you don't
- have to deal with that at all, the underlying Werkzeug library handles
- that for you.
-
-``POST``
- The browser tells the server that it wants to *post* some new
- information to that URL and that the server must ensure the data is
- stored and only stored once. This is how HTML forms usually
- transmit data to the server.
-
-``PUT``
- Similar to ``POST`` but the server might trigger the store procedure
- multiple times by overwriting the old values more than once. Now you
- might be asking why this is useful, but there are some good reasons
- to do it this way. Consider that the connection is lost during
- transmission: in this situation a system between the browser and the
- server might receive the request safely a second time without breaking
- things. With ``POST`` that would not be possible because it must only
- be triggered once.
-
-``DELETE``
- Remove the information at the given location.
-
-``OPTIONS``
- Provides a quick way for a client to figure out which methods are
- supported by this URL. Starting with Flask 0.6, this is implemented
- for you automatically.
-
-Now the interesting part is that in HTML4 and XHTML1, the only methods a
-form can submit to the server are ``GET`` and ``POST``. But with JavaScript
-and future HTML standards you can use the other methods as well. Furthermore
-HTTP has become quite popular lately and browsers are no longer the only
-clients that are using HTTP. For instance, many revision control systems
-use it.
-
-.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt
+If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method
+and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise,
+``OPTIONS`` is automatically implemented for you.
+
+.. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt
Static Files
------------
@@ -538,16 +494,16 @@ The Request Object
``````````````````
The request object is documented in the API section and we will not cover
-it here in detail (see :class:`~flask.request`). Here is a broad overview of
+it here in detail (see :class:`~flask.Request`). Here is a broad overview of
some of the most common operations. First of all you have to import it from
the ``flask`` module::
from flask import request
The current request method is available by using the
-:attr:`~flask.request.method` attribute. To access form data (data
+:attr:`~flask.Request.method` attribute. To access form data (data
transmitted in a ``POST`` or ``PUT`` request) you can use the
-:attr:`~flask.request.form` attribute. Here is a full example of the two
+:attr:`~flask.Request.form` attribute. Here is a full example of the two
attributes mentioned above::
@app.route('/login', methods=['POST', 'GET'])
@@ -570,7 +526,7 @@ error page is shown instead. So for many situations you don't have to
deal with that problem.
To access parameters submitted in the URL (``?key=value``) you can use the
-:attr:`~flask.request.args` attribute::
+:attr:`~flask.Request.args` attribute::
searchword = request.args.get('key', '')
@@ -579,7 +535,7 @@ We recommend accessing URL parameters with `get` or by catching the
bad request page in that case is not user friendly.
For a full list of methods and attributes of the request object, head over
-to the :class:`~flask.request` documentation.
+to the :class:`~flask.Request` documentation.
File Uploads
@@ -817,6 +773,9 @@ values do not persist across requests, cookies are indeed enabled, and you are
not getting a clear error message, check the size of the cookie in your page
responses compared to the size supported by web browsers.
+Besides the default client-side based sessions, if you want to handle
+sessions on the server-side instead, there are several
+Flask extensions that support this.
Message Flashing
----------------
diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst
index 51cd66f6..c3d37297 100644
--- a/docs/reqcontext.rst
+++ b/docs/reqcontext.rst
@@ -119,9 +119,9 @@ understand what is actually happening. The new behavior is quite simple:
not executed yet or at all (for example in test environments sometimes
you might want to not execute before-request callbacks).
-Now what happens on errors? In production mode if an exception is not
-caught, the 500 internal server handler is called. In development mode
-however the exception is not further processed and bubbles up to the WSGI
+Now what happens on errors? If you are not in debug mode and an exception is not
+caught, the 500 internal server handler is called. In debug mode
+however the exception is not further processed and bubbles up to the WSGI
server. That way things like the interactive debugger can provide helpful
debug information.
@@ -214,10 +214,11 @@ provide you with important information.
Starting with Flask 0.7 you have finer control over that behavior by
setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By
default it's linked to the setting of ``DEBUG``. If the application is in
-debug mode the context is preserved, in production mode it's not.
+debug mode the context is preserved. If debug mode is set to off, the context
+is not preserved.
-Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` in production mode
-as it will cause your application to leak memory on exceptions. However
+Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` if debug mode is set to off
+as it will cause your application to leak memory on exceptions. However,
it can be useful during development to get the same error preserving
-behavior as in development mode when attempting to debug an error that
+behavior as debug mode when attempting to debug an error that
only occurs under production settings.
diff --git a/docs/security.rst b/docs/security.rst
index 587bd4ef..0d4cfdeb 100644
--- a/docs/security.rst
+++ b/docs/security.rst
@@ -15,7 +15,7 @@ it JavaScript) into the context of a website. To remedy this, developers
have to properly escape text so that it cannot include arbitrary HTML
tags. For more information on that have a look at the Wikipedia article
on `Cross-Site Scripting
-`_.
+`_.
Flask configures Jinja2 to automatically escape all values unless
explicitly told otherwise. This should rule out all XSS problems caused
@@ -104,3 +104,105 @@ vulnerabilities
`_, so
this behavior was changed and :func:`~flask.jsonify` now supports serializing
arrays.
+
+Security Headers
+----------------
+
+Browsers recognize various response headers in order to control security. We
+recommend reviewing each of the headers below for use in your application.
+The `Flask-Talisman`_ extension can be used to manage HTTPS and the security
+headers for you.
+
+.. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman
+
+HTTP Strict Transport Security (HSTS)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Tells the browser to convert all HTTP requests to HTTPS, preventing
+man-in-the-middle (MITM) attacks. ::
+
+ response.haders['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
+
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
+
+Content Security Policy (CSP)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Tell the browser where it can load various types of resource from. This header
+should be used whenever possible, but requires some work to define the correct
+policy for your site. A very strict policy would be::
+
+ response.headers['Content-Security-Policy'] = "default-src: 'self'"
+
+- https://csp.withgoogle.com/docs/index.html
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+
+X-Content-Type-Options
+~~~~~~~~~~~~~~~~~~~~~~
+
+Forces the browser to honor the response content type instead of trying to
+detect it, which can be abused to generate a cross-site scripting (XSS)
+attack. ::
+
+ response.headers['X-Content-Type-Options'] = 'nosniff'
+
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
+
+X-Frame-Options
+~~~~~~~~~~~~~~~
+
+Prevents external sites from embedding your site in an ``iframe``. This
+prevents a class of attacks where clicks in the outer frame can be translated
+invisibly to clicks on your page's elements. This is also known as
+"clickjacking". ::
+
+ response.headers['X-Frame-Options'] = 'SAMEORIGIN'
+
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+
+X-XSS-Protection
+~~~~~~~~~~~~~~~~
+
+The browser will try to prevent reflected XSS attacks by not loading the page
+if the request contains something that looks like JavaScript and the response
+contains the same data. ::
+
+ response.headers['X-XSS-Protection'] = '1; mode=block'
+
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
+
+Set-Cookie options
+~~~~~~~~~~~~~~~~~~
+
+These options can be added to a ``Set-Cookie`` header to improve their
+security. Flask has configuration options to set these on the session cookie.
+They can be set on other cookies too.
+
+- ``Secure`` limits cookies to HTTPS traffic only.
+- ``HttpOnly`` protects the contents of cookies from being read with
+ JavaScript.
+- ``SameSite`` ensures that cookies can only be requested from the same
+ domain that created them. It is not supported by Flask yet.
+
+::
+
+ app.config.update(
+ SESSION_COOKIE_SECURE=True,
+ SESSION_COOKIE_HTTPONLY=True,
+ )
+
+ response.set_cookie('username', 'flask', secure=True, httponly=True)
+
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies
+
+HTTP Public Key Pinning (HPKP)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This tells the browser to authenticate with the server using only the specific
+certificate key to prevent MITM attacks.
+
+.. warning::
+ Be careful when enabling this, as it is very difficult to undo if you set up
+ or upgrade your key incorrectly.
+
+- https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning
diff --git a/docs/signals.rst b/docs/signals.rst
index 2426e920..40041491 100644
--- a/docs/signals.rst
+++ b/docs/signals.rst
@@ -27,7 +27,7 @@ executed in undefined order and do not modify any data.
The big advantage of signals over handlers is that you can safely
subscribe to them for just a split second. These temporary
-subscriptions are helpful for unittesting for example. Say you want to
+subscriptions are helpful for unit testing for example. Say you want to
know what templates were rendered as part of a request: signals allow you
to do exactly that.
@@ -45,7 +45,7 @@ signal. When you subscribe to a signal, be sure to also provide a sender
unless you really want to listen for signals from all applications. This is
especially true if you are developing an extension.
-For example, here is a helper context manager that can be used in a unittest
+For example, here is a helper context manager that can be used in a unit test
to determine which templates were rendered and what variables were passed
to the template::
diff --git a/docs/styleguide.rst b/docs/styleguide.rst
index e03e4ef5..390d5668 100644
--- a/docs/styleguide.rst
+++ b/docs/styleguide.rst
@@ -167,7 +167,7 @@ Docstring conventions:
"""
Module header:
- The module header consists of an utf-8 encoding declaration (if non
+ The module header consists of a utf-8 encoding declaration (if non
ASCII letters are used, but it is recommended all the time) and a
standard docstring::
diff --git a/docs/testing.rst b/docs/testing.rst
index 07c9aaf5..15d0d34e 100644
--- a/docs/testing.rst
+++ b/docs/testing.rst
@@ -5,23 +5,30 @@ Testing Flask Applications
**Something that is untested is broken.**
-The origin of this quote is unknown and while it is not entirely correct, it is also
-not far from the truth. Untested applications make it hard to
+The origin of this quote is unknown and while it is not entirely correct, it
+is also not far from the truth. Untested applications make it hard to
improve existing code and developers of untested applications tend to
become pretty paranoid. If an application has automated tests, you can
safely make changes and instantly know if anything breaks.
Flask provides a way to test your application by exposing the Werkzeug
test :class:`~werkzeug.test.Client` and handling the context locals for you.
-You can then use that with your favourite testing solution. In this documentation
-we will use the :mod:`unittest` package that comes pre-installed with Python.
+You can then use that with your favourite testing solution.
+
+In this documentation we will use the `pytest`_ package as the base
+framework for our tests. You can install it with ``pip``, like so::
+
+ pip install pytest
+
+.. _pytest:
+ https://pytest.org
The Application
---------------
First, we need an application to test; we will use the application from
the :ref:`tutorial`. If you don't have that application yet, get the
-sources from `the examples`_.
+source code from `the examples`_.
.. _the examples:
https://github.com/pallets/flask/tree/master/examples/flaskr/
@@ -29,90 +36,91 @@ sources from `the examples`_.
The Testing Skeleton
--------------------
-In order to test the application, we add a second module
-(:file:`flaskr_tests.py`) and create a unittest skeleton there::
+We begin by adding a tests directory under the application root. Then
+create a Python file to store our tests (:file:`test_flaskr.py`). When we
+format the filename like ``test_*.py``, it will be auto-discoverable by
+pytest.
+
+Next, we create a `pytest fixture`_ called
+:func:`client` that configures
+the application for testing and initializes a new database.::
import os
- import flaskr
- import unittest
import tempfile
- class FlaskrTestCase(unittest.TestCase):
+ import pytest
+
+ from flaskr import flaskr
+
+
+ @pytest.fixture
+ def client():
+ db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
+ flaskr.app.config['TESTING'] = True
+ client = flaskr.app.test_client()
- def setUp(self):
- self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
- flaskr.app.config['TESTING'] = True
- self.app = flaskr.app.test_client()
- with flaskr.app.app_context():
- flaskr.init_db()
+ with flaskr.app.app_context():
+ flaskr.init_db()
- def tearDown(self):
- os.close(self.db_fd)
- os.unlink(flaskr.app.config['DATABASE'])
+ yield client
- if __name__ == '__main__':
- unittest.main()
+ os.close(db_fd)
+ os.unlink(flaskr.app.config['DATABASE'])
-The code in the :meth:`~unittest.TestCase.setUp` method creates a new test
-client and initializes a new database. This function is called before
-each individual test function is run. To delete the database after the
-test, we close the file and remove it from the filesystem in the
-:meth:`~unittest.TestCase.tearDown` method. Additionally during setup the
-``TESTING`` config flag is activated. What it does is disable the error
-catching during request handling so that you get better error reports when
-performing test requests against the application.
+This client fixture will be called by each individual test. It gives us a
+simple interface to the application, where we can trigger test requests to the
+application. The client will also keep track of cookies for us.
-This test client will give us a simple interface to the application. We can
-trigger test requests to the application, and the client will also keep track
-of cookies for us.
+During setup, the ``TESTING`` config flag is activated. What
+this does is disable error catching during request handling, so that
+you get better error reports when performing test requests against the
+application.
-Because SQLite3 is filesystem-based we can easily use the tempfile module
+Because SQLite3 is filesystem-based, we can easily use the :mod:`tempfile` module
to create a temporary database and initialize it. The
:func:`~tempfile.mkstemp` function does two things for us: it returns a
low-level file handle and a random file name, the latter we use as
database name. We just have to keep the `db_fd` around so that we can use
the :func:`os.close` function to close the file.
+To delete the database after the test, the fixture closes the file and removes
+it from the filesystem.
+
If we now run the test suite, we should see the following output::
- $ python flaskr_tests.py
+ $ pytest
- ----------------------------------------------------------------------
- Ran 0 tests in 0.000s
+ ================ test session starts ================
+ rootdir: ./flask/examples/flaskr, inifile: setup.cfg
+ collected 0 items
- OK
+ =========== no tests ran in 0.07 seconds ============
-Even though it did not run any actual tests, we already know that our flaskr
+Even though it did not run any actual tests, we already know that our ``flaskr``
application is syntactically valid, otherwise the import would have died
with an exception.
+.. _pytest fixture:
+ https://docs.pytest.org/en/latest/fixture.html
+
The First Test
--------------
Now it's time to start testing the functionality of the application.
Let's check that the application shows "No entries here so far" if we
-access the root of the application (``/``). To do this, we add a new
-test method to our class, like this::
+access the root of the application (``/``). To do this, we add a new
+test function to :file:`test_flaskr.py`, like this::
- class FlaskrTestCase(unittest.TestCase):
+ def test_empty_db(client):
+ """Start with a blank database."""
- def setUp(self):
- self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
- self.app = flaskr.app.test_client()
- flaskr.init_db()
-
- def tearDown(self):
- os.close(self.db_fd)
- os.unlink(flaskr.app.config['DATABASE'])
-
- def test_empty_db(self):
- rv = self.app.get('/')
- assert b'No entries here so far' in rv.data
+ rv = client.get('/')
+ assert b'No entries here so far' in rv.data
Notice that our test functions begin with the word `test`; this allows
-:mod:`unittest` to automatically identify the method as a test to run.
+`pytest`_ to automatically identify the function as a test to run.
-By using `self.app.get` we can send an HTTP ``GET`` request to the application with
+By using ``client.get`` we can send an HTTP ``GET`` request to the application with
the given path. The return value will be a :class:`~flask.Flask.response_class` object.
We can now use the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect
the return value (as string) from the application. In this case, we ensure that
@@ -120,12 +128,15 @@ the return value (as string) from the application. In this case, we ensure that
Run it again and you should see one passing test::
- $ python flaskr_tests.py
- .
- ----------------------------------------------------------------------
- Ran 1 test in 0.034s
+ $ pytest -v
+
+ ================ test session starts ================
+ rootdir: ./flask/examples/flaskr, inifile: setup.cfg
+ collected 1 items
- OK
+ tests/test_flaskr.py::test_empty_db PASSED
+
+ ============= 1 passed in 0.10 seconds ==============
Logging In and Out
------------------
@@ -136,67 +147,78 @@ of the application. To do this, we fire some requests to the login and logout
pages with the required form data (username and password). And because the
login and logout pages redirect, we tell the client to `follow_redirects`.
-Add the following two methods to your `FlaskrTestCase` class::
+Add the following two functions to your :file:`test_flaskr.py` file::
+
+ def login(client, username, password):
+ return client.post('/login', data=dict(
+ username=username,
+ password=password
+ ), follow_redirects=True)
- def login(self, username, password):
- return self.app.post('/login', data=dict(
- username=username,
- password=password
- ), follow_redirects=True)
- def logout(self):
- return self.app.get('/logout', follow_redirects=True)
+ def logout(client):
+ return client.get('/logout', follow_redirects=True)
Now we can easily test that logging in and out works and that it fails with
-invalid credentials. Add this new test to the class::
-
- def test_login_logout(self):
- rv = self.login('admin', 'default')
- assert 'You were logged in' in rv.data
- rv = self.logout()
- assert 'You were logged out' in rv.data
- rv = self.login('adminx', 'default')
- assert 'Invalid username' in rv.data
- rv = self.login('admin', 'defaultx')
- assert 'Invalid password' in rv.data
+invalid credentials. Add this new test function::
+
+ def test_login_logout(client):
+ """Make sure login and logout works."""
+
+ rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
+ assert b'You were logged in' in rv.data
+
+ rv = logout(client)
+ assert b'You were logged out' in rv.data
+
+ rv = login(client, flaskr.app.config['USERNAME'] + 'x', flaskr.app.config['PASSWORD'])
+ assert b'Invalid username' in rv.data
+
+ rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'] + 'x')
+ assert b'Invalid password' in rv.data
Test Adding Messages
--------------------
-We should also test that adding messages works. Add a new test method
+We should also test that adding messages works. Add a new test function
like this::
- def test_messages(self):
- self.login('admin', 'default')
- rv = self.app.post('/add', data=dict(
+ def test_messages(client):
+ """Test that messages work."""
+
+ login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
+ rv = client.post('/add', data=dict(
title='',
text='HTML allowed here'
), follow_redirects=True)
- assert 'No entries here so far' not in rv.data
- assert '<Hello>' in rv.data
- assert 'HTML allowed here' in rv.data
+ assert b'No entries here so far' not in rv.data
+ assert b'<Hello>' in rv.data
+ assert b'HTML allowed here' in rv.data
Here we check that HTML is allowed in the text but not in the title,
which is the intended behavior.
Running that should now give us three passing tests::
- $ python flaskr_tests.py
- ...
- ----------------------------------------------------------------------
- Ran 3 tests in 0.332s
+ $ pytest -v
+
+ ================ test session starts ================
+ rootdir: ./flask/examples/flaskr, inifile: setup.cfg
+ collected 3 items
+
+ tests/test_flaskr.py::test_empty_db PASSED
+ tests/test_flaskr.py::test_login_logout PASSED
+ tests/test_flaskr.py::test_messages PASSED
- OK
+ ============= 3 passed in 0.23 seconds ==============
For more complex tests with headers and status codes, check out the
`MiniTwit Example`_ from the sources which contains a larger test
suite.
-
.. _MiniTwit Example:
https://github.com/pallets/flask/tree/master/examples/minitwit/
-
Other Testing Tricks
--------------------
@@ -208,7 +230,7 @@ temporarily. With this you can access the :class:`~flask.request`,
functions. Here is a full example that demonstrates this approach::
import flask
-
+
app = flask.Flask(__name__)
with app.test_request_context('/?name=Peter'):
diff --git a/docs/tutorial/css.rst b/docs/tutorial/css.rst
index ea461a89..56414657 100644
--- a/docs/tutorial/css.rst
+++ b/docs/tutorial/css.rst
@@ -1,6 +1,6 @@
.. _tutorial-css:
-Step 9: Adding Style
+Step 8: Adding Style
====================
Now that everything else works, it's time to add some style to the
diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst
index 9f4428b9..179c962b 100644
--- a/docs/tutorial/dbcon.rst
+++ b/docs/tutorial/dbcon.rst
@@ -3,7 +3,10 @@
Step 4: Database Connections
----------------------------
-You now have a function for establishing a database connection with
+Let's continue building our code in the ``flaskr.py`` file.
+(Scroll to the end of the page for more about project layout.)
+
+You currently have a function for establishing a database connection with
`connect_db`, but by itself, it is not particularly useful. Creating and
closing database connections all the time is very inefficient, so you will
need to keep it around for longer. Because database connections
diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst
index 09997906..484354ba 100644
--- a/docs/tutorial/dbinit.rst
+++ b/docs/tutorial/dbinit.rst
@@ -9,33 +9,39 @@ systems need a schema that tells them how to store that information.
Before starting the server for the first time, it's important to create
that schema.
-Such a schema can be created by piping the ``schema.sql`` file into the
-`sqlite3` command as follows::
+Such a schema could be created by piping the ``schema.sql`` file into the
+``sqlite3`` command as follows::
sqlite3 /tmp/flaskr.db < schema.sql
-The downside of this is that it requires the ``sqlite3`` command to be
-installed, which is not necessarily the case on every system. This also
-requires that you provide the path to the database, which can introduce
-errors. It's a good idea to add a function that initializes the database
-for you, to the application.
+However, the downside of this is that it requires the ``sqlite3`` command
+to be installed, which is not necessarily the case on every system. This
+also requires that you provide the path to the database, which can introduce
+errors.
-To do this, you can create a function and hook it into a :command:`flask`
-command that initializes the database. For now just take a look at the
-code segment below. A good place to add this function, and command, is
-just below the `connect_db` function in :file:`flaskr.py`::
+Instead of the ``sqlite3`` command above, it's a good idea to add a function
+to our application that initializes the database for you. To do this, you
+can create a function and hook it into a :command:`flask` command that
+initializes the database.
+
+Take a look at the code segment below. A good place to add this function,
+and command, is just below the ``connect_db`` function in :file:`flaskr.py`::
def init_db():
db = get_db()
+
with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
+
db.commit()
+
@app.cli.command('initdb')
def initdb_command():
"""Initializes the database."""
+
init_db()
- print 'Initialized the database.'
+ print('Initialized the database.')
The ``app.cli.command()`` decorator registers a new command with the
:command:`flask` script. When the command executes, Flask will automatically
@@ -59,7 +65,8 @@ On that cursor, there is a method to execute a complete script. Finally, you
only have to commit the changes. SQLite3 and other transactional
databases will not commit unless you explicitly tell it to.
-Now, it is possible to create a database with the :command:`flask` script::
+Now, in a terminal, from the application root directory :file:`flaskr/` it is
+possible to create a database with the :command:`flask` script::
flask initdb
Initialized the database.
diff --git a/docs/tutorial/folders.rst b/docs/tutorial/folders.rst
index 4e117d1f..23fefaec 100644
--- a/docs/tutorial/folders.rst
+++ b/docs/tutorial/folders.rst
@@ -3,8 +3,11 @@
Step 0: Creating The Folders
============================
-Before getting started, you will need to create the folders needed for this
-application::
+It is recommended to install your Flask application within a virtualenv. Please
+read the :ref:`installation` section to set up your environment.
+
+Now that you have installed Flask, you will need to create the folders required
+for this tutorial. Your directory structure will look like this::
/flaskr
/flaskr
@@ -13,13 +16,14 @@ application::
The application will be installed and run as Python package. This is the
recommended way to install and run Flask applications. You will see exactly
-how to run ``flaskr`` later on in this tutorial. For now go ahead and create
-the applications directory structure. In the next few steps you will be
-creating the database schema as well as the main module.
+how to run ``flaskr`` later on in this tutorial.
+
+For now go ahead and create the applications directory structure. In the next
+few steps you will be creating the database schema as well as the main module.
As a quick side note, the files inside of the :file:`static` folder are
available to users of the application via HTTP. This is the place where CSS and
-Javascript files go. Inside the :file:`templates` folder, Flask will look for
+JavaScript files go. Inside the :file:`templates` folder, Flask will look for
`Jinja2`_ templates. You will see examples of this later on.
For now you should continue with :ref:`tutorial-schema`.
diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst
index ccd4e7d2..7eee5fa0 100644
--- a/docs/tutorial/index.rst
+++ b/docs/tutorial/index.rst
@@ -3,19 +3,19 @@
Tutorial
========
-You want to develop an application with Python and Flask? Here you have
-the chance to learn by example. In this tutorial, we will create a simple
-microblogging application. It only supports one user that can create
-text-only entries and there are no feeds or comments, but it still
-features everything you need to get started. We will use Flask and SQLite
-as a database (which comes out of the box with Python) so there is nothing
-else you need.
+Learn by example to develop an application with Python and Flask.
+
+In this tutorial, we will create a simple blogging application. It only
+supports one user, only allows text entries, and has no feeds or comments.
+
+While very simple, this example still features everything you need to get
+started. In addition to Flask, we will use SQLite for the database, which is
+built-in to Python, so there is nothing else you need.
If you want the full source code in advance or for comparison, check out
the `example source`_.
-.. _example source:
- https://github.com/pallets/flask/tree/master/examples/flaskr/
+.. _example source: https://github.com/pallets/flask/tree/master/examples/flaskr/
.. toctree::
:maxdepth: 2
@@ -24,7 +24,7 @@ the `example source`_.
folders
schema
setup
- setuptools
+ packaging
dbcon
dbinit
views
diff --git a/docs/tutorial/introduction.rst b/docs/tutorial/introduction.rst
index dd46628b..67008435 100644
--- a/docs/tutorial/introduction.rst
+++ b/docs/tutorial/introduction.rst
@@ -22,7 +22,7 @@ connections in a more intelligent way, allowing you to target different
relational databases at once and more. You might also want to consider
one of the popular NoSQL databases if your data is more suited for those.
-Here a screenshot of the final application:
+Here is a screenshot of the final application:
.. image:: ../_static/flaskr.png
:align: center
@@ -31,4 +31,4 @@ Here a screenshot of the final application:
Continue with :ref:`tutorial-folders`.
-.. _SQLAlchemy: http://www.sqlalchemy.org/
+.. _SQLAlchemy: https://www.sqlalchemy.org/
diff --git a/docs/tutorial/setuptools.rst b/docs/tutorial/packaging.rst
similarity index 50%
rename from docs/tutorial/setuptools.rst
rename to docs/tutorial/packaging.rst
index 306d94d3..5db921aa 100644
--- a/docs/tutorial/setuptools.rst
+++ b/docs/tutorial/packaging.rst
@@ -1,7 +1,7 @@
-.. _tutorial-setuptools:
+.. _tutorial-packaging:
-Step 3: Installing flaskr with setuptools
-=========================================
+Step 3: Installing flaskr as a Package
+======================================
Flask is now shipped with built-in support for `Click`_. Click provides
Flask with enhanced and extensible command line utilities. Later in this
@@ -9,21 +9,23 @@ tutorial you will see exactly how to extend the ``flask`` command line
interface (CLI).
A useful pattern to manage a Flask application is to install your app
-using `setuptools`_. This involves creating a :file:`setup.py`
-in the projects root directory. You also need to add an empty
-:file:`__init__.py` file to make the :file:`flaskr/flaskr` directory
-a package. The code structure at this point should be::
+following the `Python Packaging Guide`_. Presently this involves
+creating two new files; :file:`setup.py` and :file:`MANIFEST.in` in the
+projects root directory. You also need to add an :file:`__init__.py`
+file to make the :file:`flaskr/flaskr` directory a package. After these
+changes, your code structure should be::
/flaskr
/flaskr
__init__.py
/static
/templates
+ flaskr.py
+ schema.sql
setup.py
+ MANIFEST.in
-The content of the ``setup.py`` file for ``flaskr`` is:
-
-.. sourcecode:: python
+Create the ``setup.py`` file for ``flaskr`` with the following content::
from setuptools import setup
@@ -39,38 +41,53 @@ The content of the ``setup.py`` file for ``flaskr`` is:
When using setuptools, it is also necessary to specify any special files
that should be included in your package (in the :file:`MANIFEST.in`).
In this case, the static and templates directories need to be included,
-as well as the schema. Create the :file:`MANIFEST.in` and add the
-following lines::
+as well as the schema.
+
+Create the :file:`MANIFEST.in` and add the following lines::
graft flaskr/templates
graft flaskr/static
include flaskr/schema.sql
+Next, to simplify locating the application, create the file,
+:file:`flaskr/__init__.py` containing only the following import statement::
+
+ from .flaskr import app
+
+This import statement brings the application instance into the top-level
+of the application package. When it is time to run the application, the
+Flask development server needs the location of the app instance. This
+import statement simplifies the location process. Without the above
+import statement, the export statement a few steps below would need to be
+``export FLASK_APP=flaskr.flaskr``.
+
At this point you should be able to install the application. As usual, it
is recommended to install your Flask application within a `virtualenv`_.
-With that said, go ahead and install the application with::
+With that said, from the ``flaskr/`` directory, go ahead and install the
+application with::
pip install --editable .
-.. note:: The above installation command assumes that it is run within the
- projects root directory, `flaskr/`. Also, the `editable` flag allows
- editing source code without having to reinstall the Flask app each time
- you make changes.
+The above installation command assumes that it is run within the projects
+root directory, ``flaskr/``. The ``editable`` flag allows editing
+source code without having to reinstall the Flask app each time you make
+changes. The flaskr app is now installed in your virtualenv (see output
+of ``pip freeze``).
With that out of the way, you should be able to start up the application.
-Do this with the following commands::
+Do this on Mac or Linux with the following commands in ``flaskr/``::
- export FLASK_APP=flaskr.flaskr
- export FLASK_DEBUG=1
+ export FLASK_APP=flaskr
+ export FLASK_DEBUG=true
flask run
-(In case you are on Windows you need to use `set` instead of `export`).
+(In case you are on Windows you need to use ``set`` instead of ``export``).
The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger.
*Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server!
You will see a message telling you that server has started along with
-the address at which you can access it.
+the address at which you can access it in a browser.
When you head over to the server in your browser, you will get a 404 error
because we don't have any views yet. That will be addressed a little later,
@@ -85,5 +102,5 @@ but first, you should get the database working.
Continue with :ref:`tutorial-dbcon`.
.. _Click: http://click.pocoo.org
-.. _setuptools: https://setuptools.readthedocs.io
+.. _Python Packaging Guide: https://packaging.python.org
.. _virtualenv: https://virtualenv.pypa.io
diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst
index 78b6390a..81309e65 100644
--- a/docs/tutorial/setup.rst
+++ b/docs/tutorial/setup.rst
@@ -3,27 +3,31 @@
Step 2: Application Setup Code
==============================
-Now that the schema is in place, you can create the application module,
-:file:`flaskr.py`. This file should be placed inside of the
-:file:`flaskr/flaskr` folder. The first several lines of code in the
-application module are the needed import statements. After that there will be a
-few lines of configuration code. For small applications like ``flaskr``, it is
-possible to drop the configuration directly into the module. However, a cleaner
-solution is to create a separate ``.ini`` or ``.py`` file, load that, and
-import the values from there.
+Next, we will create the application module, :file:`flaskr.py`. Just like the
+:file:`schema.sql` file you created in the previous step, this file should be
+placed inside of the :file:`flaskr/flaskr` folder.
+
+For this tutorial, all the Python code we use will be put into this file
+(except for one line in ``__init__.py``, and any testing or optional files you
+decide to create).
+
+The first several lines of code in the application module are the needed import
+statements. After that there will be a few lines of configuration code.
+
+For small applications like ``flaskr``, it is possible to drop the configuration
+directly into the module. However, a cleaner solution is to create a separate
+``.py`` file, load that, and import the values from there.
Here are the import statements (in :file:`flaskr.py`)::
- # all the imports
import os
import sqlite3
- from flask import Flask, request, session, g, redirect, url_for, abort, \
- render_template, flash
-The next couple lines will create the actual application instance and
-initialize it with the config from the same file in :file:`flaskr.py`:
+ from flask import (Flask, request, session, g, redirect, url_for, abort,
+ render_template, flash)
-.. sourcecode:: python
+The next couple lines will create the actual application instance and
+initialize it with the config from the same file in :file:`flaskr.py`::
app = Flask(__name__) # create the application instance :)
app.config.from_object(__name__) # load config from this file , flaskr.py
@@ -37,8 +41,8 @@ initialize it with the config from the same file in :file:`flaskr.py`:
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
-The :class:`~flask.Config` object works similarly to a dictionary, so it can be
-updated with new values.
+In the above code, the :class:`~flask.Config` object works similarly to a
+dictionary, so it can be updated with new values.
.. admonition:: Database Path
@@ -58,15 +62,15 @@ updated with new values.
Usually, it is a good idea to load a separate, environment-specific
configuration file. Flask allows you to import multiple configurations and it
will use the setting defined in the last import. This enables robust
-configuration setups. :meth:`~flask.Config.from_envvar` can help achieve this.
-
-.. sourcecode:: python
+configuration setups. :meth:`~flask.Config.from_envvar` can help achieve
+this. ::
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
-Simply define the environment variable :envvar:`FLASKR_SETTINGS` that points to
-a config file to be loaded. The silent switch just tells Flask to not complain
-if no such environment key is set.
+If you want to do this (not required for this tutorial) simply define the
+environment variable :envvar:`FLASKR_SETTINGS` that points to a config file
+to be loaded. The silent switch just tells Flask to not complain if no such
+environment key is set.
In addition to that, you can use the :meth:`~flask.Config.from_object`
method on the config object and provide it with an import name of a
@@ -76,22 +80,22 @@ that in all cases, only variable names that are uppercase are considered.
The ``SECRET_KEY`` is needed to keep the client-side sessions secure.
Choose that key wisely and as hard to guess and complex as possible.
-Lastly, you will add a method that allows for easy connections to the
-specified database. This can be used to open a connection on request and
-also from the interactive Python shell or a script. This will come in
-handy later. You can create a simple database connection through SQLite and
-then tell it to use the :class:`sqlite3.Row` object to represent rows.
-This allows the rows to be treated as if they were dictionaries instead of
-tuples.
-
-.. sourcecode:: python
+Lastly, add a method that allows for easy connections to the specified
+database. ::
def connect_db():
"""Connects to the specific database."""
+
rv = sqlite3.connect(app.config['DATABASE'])
rv.row_factory = sqlite3.Row
return rv
+This can be used to open a connection on request and also from the
+interactive Python shell or a script. This will come in handy later.
+You can create a simple database connection through SQLite and then tell
+it to use the :class:`sqlite3.Row` object to represent rows. This allows
+the rows to be treated as if they were dictionaries instead of tuples.
+
In the next section you will see how to run the application.
-Continue with :ref:`tutorial-setuptools`.
+Continue with :ref:`tutorial-packaging`.
diff --git a/docs/tutorial/templates.rst b/docs/tutorial/templates.rst
index d6558233..12a555e7 100644
--- a/docs/tutorial/templates.rst
+++ b/docs/tutorial/templates.rst
@@ -1,6 +1,6 @@
.. _tutorial-templates:
-Step 8: The Templates
+Step 7: The Templates
=====================
Now it is time to start working on the templates. As you may have
@@ -15,7 +15,8 @@ escaped with their XML equivalents.
We are also using template inheritance which makes it possible to reuse
the layout of the website in all pages.
-Put the following templates into the :file:`templates` folder:
+Create the follwing three HTML files and place them in the
+:file:`templates` folder:
.. _Jinja2: http://jinja.pocoo.org/docs/templates
@@ -59,7 +60,7 @@ show_entries.html
This template extends the :file:`layout.html` template from above to display the
messages. Note that the ``for`` loop iterates over the messages we passed
in with the :func:`~flask.render_template` function. Notice that the form is
-configured to to submit to the `add_entry` view function and use ``POST`` as
+configured to submit to the `add_entry` view function and use ``POST`` as
HTTP method:
.. sourcecode:: html+jinja
@@ -79,9 +80,9 @@ HTTP method:
{% endif %}
{% for entry in entries %}
- {{ entry.title }}
{{ entry.text|safe }}
+ {{ entry.title }}
{{ entry.text|safe }}
{% else %}
- - Unbelievable. No entries here so far
+
- Unbelievable. No entries here so far
{% endfor %}
{% endblock %}
diff --git a/docs/tutorial/testing.rst b/docs/tutorial/testing.rst
index c5ecf7dd..26099375 100644
--- a/docs/tutorial/testing.rst
+++ b/docs/tutorial/testing.rst
@@ -9,10 +9,10 @@ modifications in the future. The application above is used as a basic
example of how to perform unit testing in the :ref:`testing` section of the
documentation. Go there to see how easy it is to test Flask applications.
-Adding Tests to flaskr
-======================
+Adding tests to flaskr
+----------------------
-Assuming you have seen the testing section above and have either written
+Assuming you have seen the :ref:`testing` section and have either written
your own tests for ``flaskr`` or have followed along with the examples
provided, you might be wondering about ways to organize the project.
@@ -24,30 +24,38 @@ One possible and recommended project structure is::
static/
templates/
tests/
- context.py
test_flaskr.py
setup.py
MANIFEST.in
-For now go ahead a create the :file:`tests/` directory as well as the
-:file:`context.py` and :file:`test_flaskr.py` files, if you haven't
-already. The context file is used as an import helper. The contents
-of that file are::
+For now go ahead a create the :file:`tests/` directory as well as the
+:file:`test_flaskr.py` file.
- import sys, os
+Running the tests
+-----------------
- basedir = os.path.dirname(os.path.abspath(__file__))
- sys.path.insert(0, basedir + '/../')
+At this point you can run the tests. Here ``pytest`` will be used.
- from flaskr import flaskr
+.. note:: Make sure that ``pytest`` is installed in the same virtualenv
+ as flaskr. Otherwise ``pytest`` test will not be able to import the
+ required components to test the application::
-Testing + Setuptools
-====================
+ pip install -e .
+ pip install pytest
-One way to handle testing is to integrate it with ``setuptools``. All it
-requires is adding a couple of lines to the :file:`setup.py` file and
-creating a new file :file:`setup.cfg`. Go ahead and update the
-:file:`setup.py` to contain::
+Run and watch the tests pass, within the top-level :file:`flaskr/`
+directory as::
+
+ pytest
+
+Testing + setuptools
+--------------------
+
+One way to handle testing is to integrate it with ``setuptools``. Here
+that requires adding a couple of lines to the :file:`setup.py` file and
+creating a new file :file:`setup.cfg`. One benefit of running the tests
+this way is that you do not have to install ``pytest``. Go ahead and
+update the :file:`setup.py` file to contain::
from setuptools import setup
@@ -58,7 +66,6 @@ creating a new file :file:`setup.cfg`. Go ahead and update the
install_requires=[
'flask',
],
- )
setup_requires=[
'pytest-runner',
],
@@ -66,6 +73,7 @@ creating a new file :file:`setup.cfg`. Go ahead and update the
'pytest',
],
)
+
Now create :file:`setup.cfg` in the project root (alongside
:file:`setup.py`)::
@@ -85,4 +93,4 @@ found, run, and hopefully pass.
This is one possible way to run and manage testing. Here ``pytest`` is
used, but there are other options such as ``nose``. Integrating testing
with ``setuptools`` is convenient because it is not necessary to actually
-download ``pytest`` or any other testing framework one might use.
\ No newline at end of file
+download ``pytest`` or any other testing framework one might use.
diff --git a/docs/tutorial/views.rst b/docs/tutorial/views.rst
index d9838073..1b09fcb8 100644
--- a/docs/tutorial/views.rst
+++ b/docs/tutorial/views.rst
@@ -1,10 +1,11 @@
.. _tutorial-views:
-Step 7: The View Functions
+Step 6: The View Functions
==========================
Now that the database connections are working, you can start writing the
-view functions. You will need four of them:
+view functions. You will need four of them; Show Entries, Add New Entry,
+Login and Logout. Add the following code snipets to :file:`flaskr.py`.
Show Entries
------------
diff --git a/docs/upgrading.rst b/docs/upgrading.rst
index a85fb0fa..af2383c0 100644
--- a/docs/upgrading.rst
+++ b/docs/upgrading.rst
@@ -19,7 +19,45 @@ providing the ``--upgrade`` parameter::
$ pip install --upgrade Flask
-.. _upgrading-to-10:
+.. _upgrading-to-012:
+
+Version 0.12
+------------
+
+Changes to send_file
+````````````````````
+
+The ``filename`` is no longer automatically inferred from file-like objects.
+This means that the following code will no longer automatically have
+``X-Sendfile`` support, etag generation or MIME-type guessing::
+
+ response = send_file(open('/path/to/file.txt'))
+
+Any of the following is functionally equivalent::
+
+ fname = '/path/to/file.txt'
+
+ # Just pass the filepath directly
+ response = send_file(fname)
+
+ # Set the MIME-type and ETag explicitly
+ response = send_file(open(fname), mimetype='text/plain')
+ response.set_etag(...)
+
+ # Set `attachment_filename` for MIME-type guessing
+ # ETag still needs to be manually set
+ response = send_file(open(fname), attachment_filename=fname)
+ response.set_etag(...)
+
+The reason for this is that some file-like objects have an invalid or even
+misleading ``name`` attribute. Silently swallowing errors in such cases was not
+a satisfying solution.
+
+Additionally the default of falling back to ``application/octet-stream`` has
+been restricted. If Flask can't guess one or the user didn't provide one, the
+function fails if no filename information was provided.
+
+.. _upgrading-to-011:
Version 0.11
------------
@@ -105,7 +143,7 @@ when there is no request context yet but an application context. The old
``flask.Flask.request_globals_class`` attribute was renamed to
:attr:`flask.Flask.app_ctx_globals_class`.
-.. _Flask-OldSessions: http://pythonhosted.org/Flask-OldSessions/
+.. _Flask-OldSessions: https://pythonhosted.org/Flask-OldSessions/
Version 0.9
-----------
@@ -160,7 +198,7 @@ applications with Flask. Because we want to make upgrading as easy as
possible we tried to counter the problems arising from these changes by
providing a script that can ease the transition.
-The script scans your whole application and generates an unified diff with
+The script scans your whole application and generates a unified diff with
changes it assumes are safe to apply. However as this is an automated
tool it won't be able to find all use cases and it might miss some. We
internally spread a lot of deprecation warnings all over the place to make
diff --git a/examples/flaskr/README b/examples/flaskr/README
index 3cb021e7..f60287fa 100644
--- a/examples/flaskr/README
+++ b/examples/flaskr/README
@@ -9,9 +9,11 @@
~ How do I use it?
- 1. edit the configuration in the flaskr.py file or
+ 1. edit the configuration in the factory.py file or
export an FLASKR_SETTINGS environment variable
- pointing to a configuration file.
+ pointing to a configuration file or pass in a
+ dictionary with config values using the create_app
+ function.
2. install the app from the root of the project directory
@@ -19,7 +21,7 @@
3. Instruct flask to use the right application
- export FLASK_APP=flaskr.flaskr
+ export FLASK_APP=flaskr.factory:create_app()
4. initialize the database with this command:
diff --git a/examples/flaskr/flaskr/blueprints/__init__.py b/examples/flaskr/flaskr/blueprints/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/flaskr/flaskr/flaskr.py b/examples/flaskr/flaskr/blueprints/flaskr.py
similarity index 55%
rename from examples/flaskr/flaskr/flaskr.py
rename to examples/flaskr/flaskr/blueprints/flaskr.py
index b4c1d6bd..7b64dd9e 100644
--- a/examples/flaskr/flaskr/flaskr.py
+++ b/examples/flaskr/flaskr/blueprints/flaskr.py
@@ -10,29 +10,18 @@
:license: BSD, see LICENSE for more details.
"""
-import os
from sqlite3 import dbapi2 as sqlite3
-from flask import Flask, request, session, g, redirect, url_for, abort, \
- render_template, flash
+from flask import Blueprint, request, session, g, redirect, url_for, abort, \
+ render_template, flash, current_app
-# create our little application :)
-app = Flask(__name__)
-
-# Load default config and override config from an environment variable
-app.config.update(dict(
- DATABASE=os.path.join(app.root_path, 'flaskr.db'),
- DEBUG=True,
- SECRET_KEY='development key',
- USERNAME='admin',
- PASSWORD='default'
-))
-app.config.from_envvar('FLASKR_SETTINGS', silent=True)
+# create our blueprint :)
+bp = Blueprint('flaskr', __name__)
def connect_db():
"""Connects to the specific database."""
- rv = sqlite3.connect(app.config['DATABASE'])
+ rv = sqlite3.connect(current_app.config['DATABASE'])
rv.row_factory = sqlite3.Row
return rv
@@ -40,18 +29,11 @@ def connect_db():
def init_db():
"""Initializes the database."""
db = get_db()
- with app.open_resource('schema.sql', mode='r') as f:
+ with current_app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
db.commit()
-@app.cli.command('initdb')
-def initdb_command():
- """Creates the database tables."""
- init_db()
- print('Initialized the database.')
-
-
def get_db():
"""Opens a new database connection if there is none yet for the
current application context.
@@ -61,14 +43,7 @@ def get_db():
return g.sqlite_db
-@app.teardown_appcontext
-def close_db(error):
- """Closes the database again at the end of the request."""
- if hasattr(g, 'sqlite_db'):
- g.sqlite_db.close()
-
-
-@app.route('/')
+@bp.route('/')
def show_entries():
db = get_db()
cur = db.execute('select title, text from entries order by id desc')
@@ -76,7 +51,7 @@ def show_entries():
return render_template('show_entries.html', entries=entries)
-@app.route('/add', methods=['POST'])
+@bp.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
@@ -85,26 +60,26 @@ def add_entry():
[request.form['title'], request.form['text']])
db.commit()
flash('New entry was successfully posted')
- return redirect(url_for('show_entries'))
+ return redirect(url_for('flaskr.show_entries'))
-@app.route('/login', methods=['GET', 'POST'])
+@bp.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
- if request.form['username'] != app.config['USERNAME']:
+ if request.form['username'] != current_app.config['USERNAME']:
error = 'Invalid username'
- elif request.form['password'] != app.config['PASSWORD']:
+ elif request.form['password'] != current_app.config['PASSWORD']:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
- return redirect(url_for('show_entries'))
+ return redirect(url_for('flaskr.show_entries'))
return render_template('login.html', error=error)
-@app.route('/logout')
+@bp.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
- return redirect(url_for('show_entries'))
+ return redirect(url_for('flaskr.show_entries'))
diff --git a/examples/flaskr/flaskr/factory.py b/examples/flaskr/flaskr/factory.py
new file mode 100644
index 00000000..07de0aa6
--- /dev/null
+++ b/examples/flaskr/flaskr/factory.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+"""
+ Flaskr
+ ~~~~~~
+
+ A microblog example application written as Flask tutorial with
+ Flask and sqlite3.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+from flask import Flask, g
+from werkzeug.utils import find_modules, import_string
+from flaskr.blueprints.flaskr import init_db
+
+
+def create_app(config=None):
+ app = Flask('flaskr')
+
+ app.config.update(dict(
+ DATABASE=os.path.join(app.root_path, 'flaskr.db'),
+ DEBUG=True,
+ SECRET_KEY='development key',
+ USERNAME='admin',
+ PASSWORD='default'
+ ))
+ app.config.update(config or {})
+ app.config.from_envvar('FLASKR_SETTINGS', silent=True)
+
+ register_blueprints(app)
+ register_cli(app)
+ register_teardowns(app)
+
+ return app
+
+
+def register_blueprints(app):
+ """Register all blueprint modules
+
+ Reference: Armin Ronacher, "Flask for Fun and for Profit" PyBay 2016.
+ """
+ for name in find_modules('flaskr.blueprints'):
+ mod = import_string(name)
+ if hasattr(mod, 'bp'):
+ app.register_blueprint(mod.bp)
+ return None
+
+
+def register_cli(app):
+ @app.cli.command('initdb')
+ def initdb_command():
+ """Creates the database tables."""
+ init_db()
+ print('Initialized the database.')
+
+
+def register_teardowns(app):
+ @app.teardown_appcontext
+ def close_db(error):
+ """Closes the database again at the end of the request."""
+ if hasattr(g, 'sqlite_db'):
+ g.sqlite_db.close()
diff --git a/examples/flaskr/flaskr/templates/layout.html b/examples/flaskr/flaskr/templates/layout.html
index 737b51b2..862a9f4a 100644
--- a/examples/flaskr/flaskr/templates/layout.html
+++ b/examples/flaskr/flaskr/templates/layout.html
@@ -5,9 +5,9 @@
Flaskr
{% for message in get_flashed_messages() %}
diff --git a/examples/flaskr/flaskr/templates/login.html b/examples/flaskr/flaskr/templates/login.html
index ed09aeba..505d2f66 100644
--- a/examples/flaskr/flaskr/templates/login.html
+++ b/examples/flaskr/flaskr/templates/login.html
@@ -2,7 +2,7 @@
{% block body %}
Login
{% if error %}Error: {{ error }}{% endif %}
-