forked from orbit-oss/flask
resolve merge conflicts
This commit is contained in:
commit
31174fecd2
168 changed files with 6384 additions and 3932 deletions
BIN
docs/_static/pycharm-runconfig.png
vendored
Normal file
BIN
docs/_static/pycharm-runconfig.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
8
docs/_templates/sidebarintro.html
vendored
8
docs/_templates/sidebarintro.html
vendored
|
|
@ -1,6 +1,6 @@
|
|||
<h3>About Flask</h3>
|
||||
<p>
|
||||
Flask is a micro webdevelopment framework for Python. You are currently
|
||||
Flask is a micro web development framework for Python. You are currently
|
||||
looking at the documentation of the development version.
|
||||
</p>
|
||||
<h3>Other Formats</h3>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li>
|
||||
<li><a href="http://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
|
||||
<li><a href="http://github.com/mitsuhiko/flask">Flask @ GitHub</a></li>
|
||||
<li><a href="http://github.com/mitsuhiko/flask/issues">Issue Tracker</a></li>
|
||||
<li><a href="https://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
|
||||
<li><a href="https://github.com/pallets/flask">Flask @ GitHub</a></li>
|
||||
<li><a href="https://github.com/pallets/flask/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -46,24 +46,10 @@ spam, links to malicious software, and the like.
|
|||
Flask is no different from any other framework in that you the developer must
|
||||
build with caution, watching for exploits when building to your requirements.
|
||||
|
||||
The Status of Python 3
|
||||
----------------------
|
||||
Python 3 Support in Flask
|
||||
-------------------------
|
||||
|
||||
Currently the Python community is in the process of improving libraries to
|
||||
support the new iteration of the Python programming language. While the
|
||||
situation is greatly improving there are still some issues that make it
|
||||
hard for users to switch over to Python 3 just now. These problems are
|
||||
partially caused by changes in the language that went unreviewed for too
|
||||
long, partially also because we have not quite worked out how the lower-
|
||||
level API should change to account for the Unicode differences in Python 3.
|
||||
|
||||
We strongly recommend using Python 2.7 with activated Python 3
|
||||
warnings during development. If you plan on upgrading to Python 3 in the
|
||||
near future we strongly recommend that you read `How to write forwards
|
||||
compatible Python code
|
||||
<http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/>`_.
|
||||
|
||||
If you do want to dive into Python 3 already have a look at the
|
||||
:ref:`python3-support` page.
|
||||
Flask, its dependencies, and most Flask extensions all support Python 3.
|
||||
If you want to use Flask with Python 3 have a look at the :ref:`python3-support` page.
|
||||
|
||||
Continue to :ref:`installation` or the :ref:`quickstart`.
|
||||
|
|
|
|||
223
docs/api.rst
223
docs/api.rst
|
|
@ -30,61 +30,12 @@ Incoming Request Data
|
|||
|
||||
.. autoclass:: Request
|
||||
:members:
|
||||
|
||||
.. attribute:: form
|
||||
|
||||
A :class:`~werkzeug.datastructures.MultiDict` with the parsed form data from ``POST``
|
||||
or ``PUT`` requests. Please keep in mind that file uploads will not
|
||||
end up here, but instead in the :attr:`files` attribute.
|
||||
|
||||
.. attribute:: args
|
||||
|
||||
A :class:`~werkzeug.datastructures.MultiDict` with the parsed contents of the query
|
||||
string. (The part in the URL after the question mark).
|
||||
|
||||
.. attribute:: values
|
||||
|
||||
A :class:`~werkzeug.datastructures.CombinedMultiDict` with the contents of both
|
||||
:attr:`form` and :attr:`args`.
|
||||
|
||||
.. attribute:: cookies
|
||||
|
||||
A :class:`dict` with the contents of all cookies transmitted with
|
||||
the request.
|
||||
|
||||
.. attribute:: stream
|
||||
|
||||
If the incoming form data was not encoded with a known mimetype
|
||||
the data is stored unmodified in this stream for consumption. Most
|
||||
of the time it is a better idea to use :attr:`data` which will give
|
||||
you that data as a string. The stream only returns the data once.
|
||||
|
||||
.. attribute:: headers
|
||||
|
||||
The incoming request headers as a dictionary like object.
|
||||
|
||||
.. attribute:: data
|
||||
|
||||
Contains the incoming request data as string in case it came with
|
||||
a mimetype Flask does not handle.
|
||||
|
||||
.. attribute:: files
|
||||
|
||||
A :class:`~werkzeug.datastructures.MultiDict` with files uploaded as part of a
|
||||
``POST`` or ``PUT`` request. Each file is stored as
|
||||
:class:`~werkzeug.datastructures.FileStorage` object. It basically behaves like a
|
||||
standard file object you know from Python, with the difference that
|
||||
it also has a :meth:`~werkzeug.datastructures.FileStorage.save` function that can
|
||||
store the file on the filesystem.
|
||||
:inherited-members:
|
||||
|
||||
.. attribute:: environ
|
||||
|
||||
The underlying WSGI environment.
|
||||
|
||||
.. attribute:: method
|
||||
|
||||
The current request method (``POST``, ``GET`` etc.)
|
||||
|
||||
.. attribute:: path
|
||||
.. attribute:: full_path
|
||||
.. attribute:: script_root
|
||||
|
|
@ -114,15 +65,8 @@ Incoming Request Data
|
|||
`url_root` ``u'http://www.example.com/myapplication/'``
|
||||
============= ======================================================
|
||||
|
||||
.. attribute:: is_xhr
|
||||
|
||||
``True`` if the request was triggered via a JavaScript
|
||||
`XMLHttpRequest`. This only works with libraries that support the
|
||||
``X-Requested-With`` header and set it to `XMLHttpRequest`.
|
||||
Libraries that do that are prototype, jQuery and Mochikit and
|
||||
probably some more.
|
||||
|
||||
.. class:: request
|
||||
.. attribute:: request
|
||||
|
||||
To access incoming request data, you can use the global `request`
|
||||
object. Flask parses incoming request data for you and gives you
|
||||
|
|
@ -289,6 +233,9 @@ thing, like it does for :class:`request` and :class:`session`.
|
|||
It's now also possible to use the ``in`` operator on it to see if an
|
||||
attribute is defined and it yields all keys on iteration.
|
||||
|
||||
As of 0.11 you can use :meth:`pop` and :meth:`setdefault` in the same
|
||||
way you would use them on a dictionary.
|
||||
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
|
||||
|
||||
|
|
@ -313,13 +260,7 @@ Useful Functions and Classes
|
|||
|
||||
.. autofunction:: url_for
|
||||
|
||||
.. function:: abort(code)
|
||||
|
||||
Raises an :exc:`~werkzeug.exceptions.HTTPException` for the given
|
||||
status code. For example to abort request handling with a page not
|
||||
found exception, you would call ``abort(404)``.
|
||||
|
||||
:param code: the HTTP error code.
|
||||
.. autofunction:: abort
|
||||
|
||||
.. autofunction:: redirect
|
||||
|
||||
|
|
@ -508,36 +449,82 @@ Useful Internals
|
|||
.. autoclass:: flask.blueprints.BlueprintSetupState
|
||||
:members:
|
||||
|
||||
.. _core-signals-list:
|
||||
|
||||
Signals
|
||||
-------
|
||||
|
||||
.. when modifying this list, also update the one in signals.rst
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
.. data:: signals_available
|
||||
.. data:: signals.signals_available
|
||||
|
||||
``True`` if the signaling system is available. This is the case
|
||||
when `blinker`_ is installed.
|
||||
|
||||
The following signals exist in Flask:
|
||||
|
||||
.. data:: template_rendered
|
||||
|
||||
This signal is sent when a template was successfully rendered. The
|
||||
signal is invoked with the instance of the template as `template`
|
||||
and the context as dictionary (named `context`).
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_template_renders(sender, template, context, **extra):
|
||||
sender.logger.debug('Rendering template "%s" with context %s',
|
||||
template.name or 'string template',
|
||||
context)
|
||||
|
||||
from flask import template_rendered
|
||||
template_rendered.connect(log_template_renders, app)
|
||||
|
||||
.. data:: flask.before_render_template
|
||||
:noindex:
|
||||
|
||||
This signal is sent before template rendering process. The
|
||||
signal is invoked with the instance of the template as `template`
|
||||
and the context as dictionary (named `context`).
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_template_renders(sender, template, context, **extra):
|
||||
sender.logger.debug('Rendering template "%s" with context %s',
|
||||
template.name or 'string template',
|
||||
context)
|
||||
|
||||
from flask import before_render_template
|
||||
before_render_template.connect(log_template_renders, app)
|
||||
|
||||
.. data:: request_started
|
||||
|
||||
This signal is sent before any request processing started but when the
|
||||
request context was set up. Because the request context is already
|
||||
This signal is sent when the request context is set up, before
|
||||
any request processing happens. Because the request context is already
|
||||
bound, the subscriber can access the request with the standard global
|
||||
proxies such as :class:`~flask.request`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_request(sender, **extra):
|
||||
sender.logger.debug('Request context is set up')
|
||||
|
||||
from flask import request_started
|
||||
request_started.connect(log_request, app)
|
||||
|
||||
.. data:: request_finished
|
||||
|
||||
This signal is sent right before the response is sent to the client.
|
||||
It is passed the response to be sent named `response`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_response(sender, response, **extra):
|
||||
sender.logger.debug('Request context is about to close down. '
|
||||
'Response: %s', response)
|
||||
|
||||
from flask import request_finished
|
||||
request_finished.connect(log_response, app)
|
||||
|
||||
.. data:: got_request_exception
|
||||
|
||||
This signal is sent when an exception happens during request processing.
|
||||
|
|
@ -545,26 +532,77 @@ Signals
|
|||
in debug mode, where no exception handling happens. The exception
|
||||
itself is passed to the subscriber as `exception`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_exception(sender, exception, **extra):
|
||||
sender.logger.debug('Got exception during processing: %s', exception)
|
||||
|
||||
from flask import got_request_exception
|
||||
got_request_exception.connect(log_exception, app)
|
||||
|
||||
.. data:: request_tearing_down
|
||||
|
||||
This signal is sent when the application is tearing down the request.
|
||||
This is always called, even if an error happened. An `exc` keyword
|
||||
argument is passed with the exception that caused the teardown.
|
||||
This signal is sent when the request is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
The `exc` parameter was added.
|
||||
Example subscriber::
|
||||
|
||||
def close_db_connection(sender, **extra):
|
||||
session.close()
|
||||
|
||||
from flask import request_tearing_down
|
||||
request_tearing_down.connect(close_db_connection, app)
|
||||
|
||||
As of Flask 0.9, this will also be passed an `exc` keyword argument
|
||||
that has a reference to the exception that caused the teardown if
|
||||
there was one.
|
||||
|
||||
.. data:: appcontext_tearing_down
|
||||
|
||||
This signal is sent when the application is tearing down the
|
||||
application context. This is always called, even if an error happened.
|
||||
An `exc` keyword argument is passed with the exception that caused the
|
||||
teardown. The sender is the application.
|
||||
This signal is sent when the app context is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def close_db_connection(sender, **extra):
|
||||
session.close()
|
||||
|
||||
from flask import appcontext_tearing_down
|
||||
appcontext_tearing_down.connect(close_db_connection, app)
|
||||
|
||||
This will also be passed an `exc` keyword argument that has a reference
|
||||
to the exception that caused the teardown if there was one.
|
||||
|
||||
.. data:: appcontext_pushed
|
||||
|
||||
This signal is sent when an application context is pushed. The sender
|
||||
is the application.
|
||||
is the application. This is usually useful for unittests in order to
|
||||
temporarily hook in information. For instance it can be used to
|
||||
set a resource early onto the `g` object.
|
||||
|
||||
Example usage::
|
||||
|
||||
from contextlib import contextmanager
|
||||
from flask import appcontext_pushed
|
||||
|
||||
@contextmanager
|
||||
def user_set(app, user):
|
||||
def handler(sender, **kwargs):
|
||||
g.user = user
|
||||
with appcontext_pushed.connected_to(handler, app):
|
||||
yield
|
||||
|
||||
And in the testcode::
|
||||
|
||||
def test_user_me(self):
|
||||
with user_set(app, 'john'):
|
||||
c = app.test_client()
|
||||
resp = c.get('/users/me')
|
||||
assert resp.data == 'username=john'
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
|
|
@ -576,17 +614,25 @@ Signals
|
|||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
|
||||
.. data:: message_flashed
|
||||
|
||||
This signal is sent when the application is flashing a message. The
|
||||
messages is sent as `message` keyword argument and the category as
|
||||
`category`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
recorded = []
|
||||
def record(sender, message, category, **extra):
|
||||
recorded.append((message, category))
|
||||
|
||||
from flask import message_flashed
|
||||
message_flashed.connect(record, app)
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. currentmodule:: None
|
||||
|
||||
.. class:: flask.signals.Namespace
|
||||
.. class:: signals.Namespace
|
||||
|
||||
An alias for :class:`blinker.base.Namespace` if blinker is available,
|
||||
otherwise a dummy class that creates fake signals. This class is
|
||||
|
|
@ -600,8 +646,10 @@ Signals
|
|||
do nothing but will fail with a :exc:`RuntimeError` for all other
|
||||
operations, including connecting.
|
||||
|
||||
|
||||
.. _blinker: https://pypi.python.org/pypi/blinker
|
||||
|
||||
|
||||
Class-Based Views
|
||||
-----------------
|
||||
|
||||
|
|
@ -641,6 +689,8 @@ The following converters are available:
|
|||
`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
|
||||
=========== ===============================================
|
||||
|
||||
Custom converters can be defined using :attr:`flask.Flask.url_map`.
|
||||
|
|
@ -780,13 +830,6 @@ Command Line Interface
|
|||
Marks a function so that an instance of :class:`ScriptInfo` is passed
|
||||
as first argument to the click callback.
|
||||
|
||||
.. autofunction:: script_info_option
|
||||
|
||||
A special decorator that informs a click callback to be passed the
|
||||
script info object as first argument. This is normally not useful
|
||||
unless you implement very special commands like the run command which
|
||||
does not want the application to be loaded yet.
|
||||
|
||||
.. autodata:: run_command
|
||||
|
||||
.. autodata:: shell_command
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ In contrast, during request handling, a couple of other rules exist:
|
|||
|
||||
There is a third state which is sitting in between a little bit.
|
||||
Sometimes you are dealing with an application in a way that is similar to
|
||||
how you interact with applications during request handling just that there
|
||||
is no request active. Consider for instance that you're sitting in an
|
||||
how you interact with applications during request handling; just that there
|
||||
is no request active. Consider, for instance, that you're sitting in an
|
||||
interactive Python shell and interacting with the application, or a
|
||||
command line application.
|
||||
|
||||
|
|
@ -74,6 +74,11 @@ The application context is also used by the :func:`~flask.url_for`
|
|||
function in case a ``SERVER_NAME`` was configured. This allows you to
|
||||
generate URLs even in the absence of a request.
|
||||
|
||||
If no request context has been pushed and an application context has
|
||||
not been explicitly set, a ``RuntimeError`` will be raised. ::
|
||||
|
||||
RuntimeError: Working outside of application context.
|
||||
|
||||
Locality of the Context
|
||||
-----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Flask started in part to demonstrate how to build your own framework on top of
|
|||
existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it
|
||||
developed, it became useful to a wide audience. As you grow your codebase,
|
||||
don't just use Flask -- understand it. Read the source. Flask's code is
|
||||
written to be read; it's documentation is published so you can use its internal
|
||||
written to be read; its documentation is published so you can use its internal
|
||||
APIs. Flask sticks to documented APIs in upstream libraries, and documents its
|
||||
internal utilities so that you can find the hook points needed for your
|
||||
project.
|
||||
|
|
@ -35,7 +35,7 @@ Subclass.
|
|||
The :class:`~flask.Flask` class has many methods designed for subclassing. You
|
||||
can quickly add or customize behavior by subclassing :class:`~flask.Flask` (see
|
||||
the linked method docs) and using that subclass wherever you instantiate an
|
||||
application class. This works well with :ref:`app-factories`.
|
||||
application class. This works well with :ref:`app-factories`. See :doc:`/patterns/subclassing` for an example.
|
||||
|
||||
Wrap with middleware.
|
||||
---------------------
|
||||
|
|
|
|||
|
|
@ -176,16 +176,25 @@ the `template_folder` parameter to the :class:`Blueprint` constructor::
|
|||
|
||||
admin = Blueprint('admin', __name__, template_folder='templates')
|
||||
|
||||
As for static files, the path can be absolute or relative to the blueprint
|
||||
resource folder. The template folder is added to the searchpath of
|
||||
templates but with a lower priority than the actual application's template
|
||||
folder. That way you can easily override templates that a blueprint
|
||||
provides in the actual application.
|
||||
For static files, the path can be absolute or relative to the blueprint
|
||||
resource folder.
|
||||
|
||||
The template folder is added to the search path of templates but with a lower
|
||||
priority than the actual application's template folder. That way you can
|
||||
easily override templates that a blueprint provides in the actual application.
|
||||
This also means that if you don't want a blueprint template to be accidentally
|
||||
overridden, make sure that no other blueprint or actual application template
|
||||
has the same relative path. When multiple blueprints provide the same relative
|
||||
template path the first blueprint registered takes precedence over the others.
|
||||
|
||||
|
||||
So if you have a blueprint in the folder ``yourapplication/admin`` and you
|
||||
want to render the template ``'admin/index.html'`` and you have provided
|
||||
``templates`` as a `template_folder` you will have to create a file like
|
||||
this: :file:`yourapplication/admin/templates/admin/index.html`.
|
||||
this: :file:`yourapplication/admin/templates/admin/index.html`. The reason
|
||||
for the extra ``admin`` folder is to avoid getting our template overridden
|
||||
by a template named ``index.html`` in the actual application template
|
||||
folder.
|
||||
|
||||
To further reiterate this: if you have a blueprint named ``admin`` and you
|
||||
want to render a template called :file:`index.html` which is specific to this
|
||||
|
|
@ -236,4 +245,22 @@ Here is an example for a "404 Page Not Found" exception::
|
|||
def page_not_found(e):
|
||||
return render_template('pages/404.html')
|
||||
|
||||
Most errorhandlers will simply work as expected; however, there is a caveat
|
||||
concerning handlers for 404 and 405 exceptions. These errorhandlers are only
|
||||
invoked from an appropriate ``raise`` statement or a call to ``abort`` in another
|
||||
of the blueprint's view functions; they are not invoked by, e.g., an invalid URL
|
||||
access. This is because the blueprint does not "own" a certain URL space, so
|
||||
the application instance has no way of knowing which blueprint errorhandler it
|
||||
should run if given an invalid URL. If you would like to execute different
|
||||
handling strategies for these errors based on URL prefixes, they may be defined
|
||||
at the application level using the ``request`` proxy object::
|
||||
|
||||
@app.errorhandler(404)
|
||||
@app.errorhandler(405)
|
||||
def _handle_api_error(ex):
|
||||
if request.path.startswith('/api/'):
|
||||
return jsonify_error(ex)
|
||||
else:
|
||||
return ex
|
||||
|
||||
More information on error handling see :ref:`errorpages`.
|
||||
|
|
|
|||
224
docs/cli.rst
224
docs/cli.rst
|
|
@ -3,11 +3,11 @@
|
|||
Command Line Interface
|
||||
======================
|
||||
|
||||
.. versionadded:: 1.0
|
||||
.. versionadded:: 0.11
|
||||
|
||||
.. currentmodule:: flask
|
||||
|
||||
One of the nice new features in Flask 1.0 is the built-in integration of
|
||||
One of the nice new features in Flask 0.11 is the built-in integration of
|
||||
the `click <http://click.pocoo.org/>`_ command line interface. This
|
||||
enables a wide range of new features for the Flask ecosystem and your own
|
||||
applications.
|
||||
|
|
@ -15,41 +15,38 @@ applications.
|
|||
Basic Usage
|
||||
-----------
|
||||
|
||||
After installation of Flask you will now find a :command:`flask` script installed
|
||||
into your virtualenv. If you don't want to install Flask or you have a
|
||||
special use-case you can also use ``python -m flask`` to accomplish exactly
|
||||
the same.
|
||||
After installation of Flask you will now find a :command:`flask` script
|
||||
installed into your virtualenv. If you don't want to install Flask or you
|
||||
have a special use-case you can also use ``python -m flask`` to accomplish
|
||||
exactly the same.
|
||||
|
||||
The way this script works is by providing access to all the commands on
|
||||
your Flask application's :attr:`Flask.cli` instance as well as some
|
||||
built-in commands that are always there. Flask extensions can also
|
||||
register more commands there if they desire so.
|
||||
|
||||
For the :command:`flask` script to work, an application needs to be discovered.
|
||||
The two most common ways are either an environment variable
|
||||
(``FLASK_APP``) or the :option:`--app` / :option:`-a` parameter. It should be the
|
||||
import path for your application or the path to a Python file. In the
|
||||
latter case Flask will attempt to setup the Python path for you
|
||||
automatically and discover the module name but that might not always work.
|
||||
For the :command:`flask` script to work, an application needs to be
|
||||
discovered. This is achieved by exporting the ``FLASK_APP`` environment
|
||||
variable. It can be either set to an import path or to a filename of a
|
||||
Python module that contains a Flask application.
|
||||
|
||||
In that imported file the name of the app needs to be called ``app`` or
|
||||
optionally be specified after a colon.
|
||||
optionally be specified after a colon. For instance
|
||||
``mymodule:application`` would tell it to use the `application` object in
|
||||
the :file:`mymodule.py` file.
|
||||
|
||||
Given a :file:`hello.py` file with the application in it named ``app`` this is
|
||||
how it can be run.
|
||||
Given a :file:`hello.py` file with the application in it named ``app``
|
||||
this is how it can be run.
|
||||
|
||||
Environment variables (On Windows use ``set`` instead of ``export``)::
|
||||
|
||||
export FLASK_APP=hello
|
||||
flask run
|
||||
|
||||
Parameters::
|
||||
Or with a filename::
|
||||
|
||||
flask --app=hello run
|
||||
|
||||
File names::
|
||||
|
||||
flask --app=hello.py run
|
||||
export FLASK_APP=/path/to/hello.py
|
||||
flask run
|
||||
|
||||
Virtualenv Integration
|
||||
----------------------
|
||||
|
|
@ -59,19 +56,33 @@ If you are constantly working with a virtualenv you can also put the
|
|||
bottom of the file. That way every time you activate your virtualenv you
|
||||
automatically also activate the correct application name.
|
||||
|
||||
Edit the activate script for the shell you use. For example:
|
||||
|
||||
Unix Bash: ``venv/bin/activate``::
|
||||
|
||||
FLASK_APP=hello
|
||||
export FLASK_APP
|
||||
|
||||
Windows CMD.exe: ``venv\Scripts\activate.bat``::
|
||||
|
||||
set "FLASK_APP=hello"
|
||||
:END
|
||||
|
||||
Debug Flag
|
||||
----------
|
||||
|
||||
The :command:`flask` script can be run with :option:`--debug` or :option:`--no-debug` to
|
||||
automatically flip the debug flag of the application. This can also be
|
||||
configured by setting ``FLASK_DEBUG`` to ``1`` or ``0``.
|
||||
The :command:`flask` script can also be instructed to enable the debug
|
||||
mode of the application automatically by exporting ``FLASK_DEBUG``. If
|
||||
set to ``1`` debug is enabled or ``0`` disables it::
|
||||
|
||||
export FLASK_DEBUG=1
|
||||
|
||||
Running a Shell
|
||||
---------------
|
||||
|
||||
To run an interactive Python shell you can use the ``shell`` command::
|
||||
|
||||
flask --app=hello shell
|
||||
flask shell
|
||||
|
||||
This will start up an interactive Python shell, setup the correct
|
||||
application context and setup the local variables in the shell. This is
|
||||
|
|
@ -86,6 +97,7 @@ easily. Flask uses `click`_ for the command interface which makes
|
|||
creating custom commands very easy. For instance if you want a shell
|
||||
command to initialize the database you can do this::
|
||||
|
||||
import click
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
|
@ -93,11 +105,11 @@ command to initialize the database you can do this::
|
|||
@app.cli.command()
|
||||
def initdb():
|
||||
"""Initialize the database."""
|
||||
print 'Init the db'
|
||||
click.echo('Init the db')
|
||||
|
||||
The command will then show up on the command line::
|
||||
|
||||
$ flask -a hello.py initdb
|
||||
$ flask initdb
|
||||
Init the db
|
||||
|
||||
Application Context
|
||||
|
|
@ -122,12 +134,12 @@ Factory Functions
|
|||
-----------------
|
||||
|
||||
In case you are using factory functions to create your application (see
|
||||
:ref:`app-factories`) you will discover that the :command:`flask` command cannot
|
||||
work with them directly. Flask won't be able to figure out how to
|
||||
:ref:`app-factories`) you will discover that the :command:`flask` command
|
||||
cannot work with them directly. Flask won't be able to figure out how to
|
||||
instantiate your application properly by itself. Because of this reason
|
||||
the recommendation is to create a separate file that instantiates
|
||||
applications. This is by far not the only way to make this work. Another
|
||||
is the :ref:`custom-scripts` support.
|
||||
applications. This is not the only way to make this work. Another is the
|
||||
:ref:`custom-scripts` support.
|
||||
|
||||
For instance if you have a factory function that creates an application
|
||||
from a filename you could make a separate file that creates such an
|
||||
|
|
@ -139,8 +151,8 @@ This could be a file named :file:`autoapp.py` with these contents::
|
|||
from yourapplication import create_app
|
||||
app = create_app(os.environ['YOURAPPLICATION_CONFIG'])
|
||||
|
||||
Once this has happened you can make the flask command automatically pick
|
||||
it up::
|
||||
Once this has happened you can make the :command:`flask` command automatically
|
||||
pick it up::
|
||||
|
||||
export YOURAPPLICATION_CONFIG=/path/to/config.cfg
|
||||
export FLASK_APP=/path/to/autoapp.py
|
||||
|
|
@ -152,9 +164,9 @@ From this point onwards :command:`flask` will find your application.
|
|||
Custom Scripts
|
||||
--------------
|
||||
|
||||
While the most common way is to use the :command:`flask` command, you can also
|
||||
make your own "driver scripts". Since Flask uses click for the scripts
|
||||
there is no reason you cannot hook these scripts into any click
|
||||
While the most common way is to use the :command:`flask` command, you can
|
||||
also make your own "driver scripts". Since Flask uses click for the
|
||||
scripts there is no reason you cannot hook these scripts into any click
|
||||
application. There is one big caveat and that is, that commands
|
||||
registered to :attr:`Flask.cli` will expect to be (indirectly at least)
|
||||
launched from a :class:`flask.cli.FlaskGroup` click group. This is
|
||||
|
|
@ -162,38 +174,32 @@ necessary so that the commands know which Flask application they have to
|
|||
work with.
|
||||
|
||||
To understand why you might want custom scripts you need to understand how
|
||||
click finds and executes the Flask application. If you use the :command:`flask`
|
||||
script you specify the application to work with on the command line or
|
||||
environment variable as an import name. This is simple but it has some
|
||||
limitations. Primarily it does not work with application factory
|
||||
functions (see :ref:`app-factories`).
|
||||
click finds and executes the Flask application. If you use the
|
||||
:command:`flask` script you specify the application to work with on the
|
||||
command line or environment variable as an import name. This is simple
|
||||
but it has some limitations. Primarily it does not work with application
|
||||
factory functions (see :ref:`app-factories`).
|
||||
|
||||
With a custom script you don't have this problem as you can fully
|
||||
customize how the application will be created. This is very useful if you
|
||||
write reusable applications that you want to ship to users and they should
|
||||
be presented with a custom management script.
|
||||
|
||||
If you are used to writing click applications this will look familiar but
|
||||
at the same time, slightly different because of how commands are loaded.
|
||||
We won't go into detail now about the differences but if you are curious
|
||||
you can have a look at the :ref:`script-info-object` section to learn all
|
||||
about it.
|
||||
|
||||
To explain all of this, here is an example :file:`manage.py` script that
|
||||
manages a hypothetical wiki application. We will go through the details
|
||||
afterwards::
|
||||
|
||||
import os
|
||||
import click
|
||||
from flask.cli import FlaskGroup, script_info_option
|
||||
from flask.cli import FlaskGroup
|
||||
|
||||
def create_wiki_app(info):
|
||||
from yourwiki import create_app
|
||||
config = info.data.get('config') or 'wikiconfig.py'
|
||||
return create_app(config=config)
|
||||
return create_app(
|
||||
config=os.environ.get('WIKI_CONFIG', 'wikiconfig.py'))
|
||||
|
||||
@click.group(cls=FlaskGroup, create_app=create_wiki_app)
|
||||
@script_info_option('--config', script_info_key='config')
|
||||
def cli(**params):
|
||||
def cli():
|
||||
"""This is a management script for the wiki application."""
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
@ -204,56 +210,94 @@ step.
|
|||
|
||||
1. First we import the ``click`` library as well as the click extensions
|
||||
from the ``flask.cli`` package. Primarily we are here interested
|
||||
in the :class:`~flask.cli.FlaskGroup` click group and the
|
||||
:func:`~flask.cli.script_info_option` decorator.
|
||||
in the :class:`~flask.cli.FlaskGroup` click group.
|
||||
2. The next thing we do is defining a function that is invoked with the
|
||||
script info object (:ref:`script-info-object`) from Flask and its
|
||||
script info object (:class:`~flask.cli.ScriptInfo`) from Flask and its
|
||||
purpose is to fully import and create the application. This can
|
||||
either directly import an application object or create it (see
|
||||
:ref:`app-factories`).
|
||||
|
||||
What is ``info.data``? It's a dictionary of arbitrary data on the
|
||||
script info that can be filled by options or through other means. We
|
||||
will come back to this later.
|
||||
:ref:`app-factories`). In this case we load the config from an
|
||||
environment variable.
|
||||
3. Next step is to create a :class:`FlaskGroup`. In this case we just
|
||||
make an empty function with a help doc string that just does nothing
|
||||
and then pass the ``create_wiki_app`` function as a factory function.
|
||||
|
||||
Whenever click now needs to operate on a Flask application it will
|
||||
call that function with the script info and ask for it to be created.
|
||||
4. In step 2 you could see that the config is passed to the actual
|
||||
creation function. This config comes from the :func:`script_info_option`
|
||||
decorator for the main script. It accepts a :option:`--config` option and
|
||||
then stores it in the script info so we can use it to create the
|
||||
application.
|
||||
5. All is rounded up by invoking the script.
|
||||
4. All is rounded up by invoking the script.
|
||||
|
||||
.. _script-info-object:
|
||||
CLI Plugins
|
||||
-----------
|
||||
|
||||
The Script Info
|
||||
---------------
|
||||
Flask extensions can always patch the :attr:`Flask.cli` instance with more
|
||||
commands if they want. However there is a second way to add CLI plugins
|
||||
to Flask which is through ``setuptools``. If you make a Python package that
|
||||
should export a Flask command line plugin you can ship a :file:`setup.py` file
|
||||
that declares an entrypoint that points to a click command:
|
||||
|
||||
The Flask script integration might be confusing at first, but there is a reason
|
||||
why it's done this way. The reason for this is that Flask wants to
|
||||
both provide custom commands to click as well as not loading your
|
||||
application unless it has to. The reason for this is added flexibility.
|
||||
Example :file:`setup.py`::
|
||||
|
||||
This way an application can provide custom commands, but even in the
|
||||
absence of an application the :command:`flask` script is still operational on a
|
||||
basic level. In addition to that it means that the individual commands
|
||||
have the option to avoid creating an instance of the Flask application
|
||||
unless required. This is very useful as it allows the server commands for
|
||||
instance to load the application on a first request instead of
|
||||
immediately, therefore giving a better debug experience.
|
||||
from setuptools import setup
|
||||
|
||||
All of this is provided through the :class:`flask.cli.ScriptInfo` object
|
||||
and some helper utilities around. The basic way it operates is that when
|
||||
the :class:`flask.cli.FlaskGroup` executes as a script it creates a script
|
||||
info and keeps it around. From that point onwards modifications on the
|
||||
script info can be done through click options. To simplify this pattern
|
||||
the :func:`flask.cli.script_info_option` decorator was added.
|
||||
setup(
|
||||
name='flask-my-extension',
|
||||
...
|
||||
entry_points='''
|
||||
[flask.commands]
|
||||
my-command=mypackage.commands:cli
|
||||
''',
|
||||
)
|
||||
|
||||
Once Flask actually needs the individual Flask application it will invoke
|
||||
the :meth:`flask.cli.ScriptInfo.load_app` method. This happens when the
|
||||
server starts, when the shell is launched or when the script looks for an
|
||||
application-provided click command.
|
||||
Inside :file:`mypackage/commands.py` you can then export a Click object::
|
||||
|
||||
import click
|
||||
|
||||
@click.command()
|
||||
def cli():
|
||||
"""This is an example command."""
|
||||
|
||||
Once that package is installed in the same virtualenv as Flask itself you
|
||||
can run ``flask my-command`` to invoke your command. This is useful to
|
||||
provide extra functionality that Flask itself cannot ship.
|
||||
|
||||
PyCharm Integration
|
||||
-------------------
|
||||
|
||||
The new Flask CLI features aren’t yet fully integrated into the PyCharm IDE,
|
||||
so we have to do a few tweaks to get them working smoothly.
|
||||
|
||||
In your PyCharm application, with your project open, click on *Run*
|
||||
from the menu bar and go to *Edit Configurations*. You’ll be greeted by a
|
||||
screen similar to this:
|
||||
|
||||
.. image:: _static/pycharm-runconfig.png
|
||||
:align: center
|
||||
:class: screenshot
|
||||
:alt: screenshot of pycharm's run configuration settings
|
||||
|
||||
There’s quite a few options to change, but don’t worry— once we’ve done it
|
||||
for one command, we can easily copy the entire configuration and make a
|
||||
single tweak to give us access to other flask cli commands, including
|
||||
any custom ones you may implement yourself.
|
||||
|
||||
For the *Script* input (**A**), we want to navigate to the virtual environment
|
||||
we’re using for our project and within that folder we want to pick the ``flask``
|
||||
file which will reside in the ``bin`` folder, or in the ``Scripts`` folder if
|
||||
you're on Windows.
|
||||
|
||||
The *Script Parameter* field (**B**) is set to the cli command you wish to
|
||||
execute, in this example we use ``run`` which will run our development server.
|
||||
|
||||
We need to add an environment variable (**C**) to identify our application.
|
||||
Click on the browse button and add an entry with ``FLASK_APP`` on the
|
||||
left and the name of the python file, or package on the right
|
||||
(``app.py`` for example).
|
||||
|
||||
Next we need to set the working directory (**D**) to be the same folder where
|
||||
our application file or package resides.
|
||||
|
||||
Finally, untick the *PYTHONPATH* options (**E**) and give the configuration a
|
||||
good descriptive name, such as “Run Flask Server” and click *Apply*.
|
||||
|
||||
Now that we have on run configuration which implements ``flask run`` from within
|
||||
PyCharm, we can simply copy that configuration and alter the script argument
|
||||
to run a different cli command, e.g. ``flask shell``.
|
||||
|
|
|
|||
116
docs/conf.py
116
docs/conf.py
|
|
@ -10,14 +10,20 @@
|
|||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import pkg_resources
|
||||
import time
|
||||
import datetime
|
||||
|
||||
import sys, os
|
||||
BUILD_DATE = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
sys.path.append(os.path.abspath('.'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '_themes'))
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
|
|
@ -26,8 +32,19 @@ sys.path.append(os.path.abspath('.'))
|
|||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
|
||||
'flaskdocext']
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'flaskdocext'
|
||||
]
|
||||
|
||||
try:
|
||||
__import__('sphinxcontrib.log_cabinet')
|
||||
except ImportError:
|
||||
print('sphinxcontrib-log-cabinet is not installed.')
|
||||
print('Changelog directives will not be re-organized.')
|
||||
else:
|
||||
extensions.append('sphinxcontrib.log_cabinet')
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
|
@ -43,24 +60,21 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'Flask'
|
||||
copyright = u'2015, Armin Ronacher'
|
||||
copyright = u'2010 - {0}, Armin Ronacher'.format(BUILD_DATE.year)
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
import pkg_resources
|
||||
try:
|
||||
release = pkg_resources.get_distribution('Flask').version
|
||||
except pkg_resources.DistributionNotFound:
|
||||
print 'To build the documentation, The distribution information of Flask'
|
||||
print 'Has to be available. Either install the package into your'
|
||||
print 'development environment or run "setup.py develop" to setup the'
|
||||
print 'metadata. A virtualenv is recommended!'
|
||||
print('Flask must be installed to build the documentation.')
|
||||
print('Install from source using `pip install -e .` in a virtualenv.')
|
||||
sys.exit(1)
|
||||
del pkg_resources
|
||||
|
||||
if 'dev' in release:
|
||||
release = release.split('dev')[0] + 'dev'
|
||||
release = ''.join(release.partition('dev')[:2])
|
||||
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
|
|
@ -99,14 +113,12 @@ exclude_patterns = ['_build']
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'flask'
|
||||
# html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {
|
||||
'touch_icon': 'touch-icon.png'
|
||||
}
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_themes']
|
||||
|
|
@ -125,7 +137,7 @@ html_theme_path = ['_themes']
|
|||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
html_favicon = "flask-favicon.ico"
|
||||
html_favicon = '_static/flask-favicon.ico'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
|
@ -142,9 +154,18 @@ html_static_path = ['_static']
|
|||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html']
|
||||
'index': [
|
||||
'sidebarintro.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html'
|
||||
],
|
||||
'**': [
|
||||
'sidebarlogo.html',
|
||||
'localtoc.html',
|
||||
'relations.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html'
|
||||
]
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
|
|
@ -186,8 +207,7 @@ htmlhelp_basename = 'Flaskdoc'
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('latexindex', 'Flask.tex', u'Flask Documentation',
|
||||
u'Armin Ronacher', 'manual'),
|
||||
('latexindex', 'Flask.tex', u'Flask Documentation', u'Armin Ronacher', 'manual'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
|
@ -197,10 +217,10 @@ latex_documents = [
|
|||
latex_use_modindex = False
|
||||
|
||||
latex_elements = {
|
||||
'fontpkg': r'\usepackage{mathpazo}',
|
||||
'papersize': 'a4paper',
|
||||
'pointsize': '12pt',
|
||||
'preamble': r'\usepackage{flaskstyle}'
|
||||
'fontpkg': r'\usepackage{mathpazo}',
|
||||
'papersize': 'a4paper',
|
||||
'pointsize': '12pt',
|
||||
'preamble': r'\usepackage{flaskstyle}'
|
||||
}
|
||||
latex_use_parts = True
|
||||
|
||||
|
|
@ -222,7 +242,7 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
|
|||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
#epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# The unique identifier of the text. This can be an ISBN number
|
||||
# or the project homepage.
|
||||
#epub_identifier = ''
|
||||
|
||||
|
|
@ -244,31 +264,29 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
|
|||
#epub_tocdepth = 3
|
||||
|
||||
intersphinx_mapping = {
|
||||
'https://docs.python.org/dev': None,
|
||||
'http://werkzeug.pocoo.org/docs/': None,
|
||||
'http://click.pocoo.org/': None,
|
||||
'http://jinja.pocoo.org/docs/': None,
|
||||
'http://www.sqlalchemy.org/docs/': None,
|
||||
'https://wtforms.readthedocs.org/en/latest/': None,
|
||||
'https://pythonhosted.org/blinker/': None
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'werkzeug': ('http://werkzeug.pocoo.org/docs/', None),
|
||||
'click': ('http://click.pocoo.org/', None),
|
||||
'jinja': ('http://jinja.pocoo.org/docs/', None),
|
||||
'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None),
|
||||
'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None),
|
||||
'blinker': ('https://pythonhosted.org/blinker/', None)
|
||||
}
|
||||
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
|
||||
# fall back if theme is not there
|
||||
try:
|
||||
__import__('flask_theme_support')
|
||||
except ImportError, e:
|
||||
print '-' * 74
|
||||
print 'Warning: Flask themes unavailable. Building with default theme'
|
||||
print 'If you want the Flask themes, run this command and build again:'
|
||||
print
|
||||
print ' git submodule update --init'
|
||||
print '-' * 74
|
||||
|
||||
pygments_style = 'tango'
|
||||
html_theme = 'default'
|
||||
html_theme_options = {}
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
html_theme = 'flask'
|
||||
html_theme_options = {
|
||||
'touch_icon': 'touch-icon.png'
|
||||
}
|
||||
except ImportError:
|
||||
print('-' * 74)
|
||||
print('Warning: Flask themes unavailable. Building with default theme')
|
||||
print('If you want the Flask themes, run this command and build again:')
|
||||
print()
|
||||
print(' git submodule update --init')
|
||||
print('-' * 74)
|
||||
|
||||
|
||||
# unwrap decorators
|
||||
|
|
|
|||
|
|
@ -44,6 +44,21 @@ method::
|
|||
SECRET_KEY='...'
|
||||
)
|
||||
|
||||
.. admonition:: Debug Mode with the ``flask`` Script
|
||||
|
||||
If you use the :command:`flask` script to start a local development
|
||||
server, to enable the debug mode, you need to export the ``FLASK_DEBUG``
|
||||
environment variable before running the server::
|
||||
|
||||
$ export FLASK_DEBUG=1
|
||||
$ flask run
|
||||
|
||||
(On Windows you need to use ``set`` instead of ``export``).
|
||||
|
||||
``app.debug`` and ``app.config['DEBUG']`` are not compatible with
|
||||
the :command:`flask` script. They only worked when using ``Flask.run()``
|
||||
method.
|
||||
|
||||
Builtin Configuration Values
|
||||
----------------------------
|
||||
|
||||
|
|
@ -52,7 +67,8 @@ The following configuration values are used internally by Flask:
|
|||
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
|
||||
|
||||
================================= =========================================
|
||||
``DEBUG`` enable/disable debug mode
|
||||
``DEBUG`` enable/disable debug mode when using
|
||||
``Flask.run()`` method to start server
|
||||
``TESTING`` enable/disable testing mode
|
||||
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
|
||||
propagation of exceptions. If not set or
|
||||
|
|
@ -116,13 +132,13 @@ The following configuration values are used internally by Flask:
|
|||
by default enables URL generation
|
||||
without a request context but with an
|
||||
application context.
|
||||
``APPLICATION_ROOT`` If the application does not occupy
|
||||
a whole domain or subdomain this can
|
||||
be set to the path where the application
|
||||
is configured to live. This is for
|
||||
session cookie as path value. If
|
||||
domains are used, this should be
|
||||
``None``.
|
||||
``APPLICATION_ROOT`` The path value used for the session
|
||||
cookie if ``SESSION_COOKIE_PATH`` isn't
|
||||
set. If it's also ``None`` ``'/'`` is used.
|
||||
Note that to actually serve your Flask
|
||||
app under a subpath you need to tell
|
||||
your WSGI container the ``SCRIPT_NAME``
|
||||
WSGI environment variable.
|
||||
``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will
|
||||
reject incoming requests with a
|
||||
content length greater than this by
|
||||
|
|
@ -130,8 +146,9 @@ The following configuration values are used internally by Flask:
|
|||
``SEND_FILE_MAX_AGE_DEFAULT`` Default cache control max age to use with
|
||||
:meth:`~flask.Flask.send_static_file` (the
|
||||
default static file handler) and
|
||||
:func:`~flask.send_file`, in
|
||||
seconds. Override this value on a per-file
|
||||
:func:`~flask.send_file`, as
|
||||
:class:`datetime.timedelta` or as seconds.
|
||||
Override this value on a per-file
|
||||
basis using the
|
||||
:meth:`~flask.Flask.get_send_file_max_age`
|
||||
hook on :class:`~flask.Flask` or
|
||||
|
|
@ -176,12 +193,11 @@ The following configuration values are used internally by Flask:
|
|||
behavior by changing this variable.
|
||||
This is not recommended but might give
|
||||
you a performance improvement on the
|
||||
cost of cachability.
|
||||
``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` (the default)
|
||||
jsonify responses will be pretty printed
|
||||
if they are not requested by an
|
||||
XMLHttpRequest object (controlled by
|
||||
the ``X-Requested-With`` header)
|
||||
cost of cacheability.
|
||||
``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` or the Flask app
|
||||
is running in debug mode, jsonify responses
|
||||
will be pretty printed.
|
||||
``JSONIFY_MIMETYPE`` MIME type used for jsonify responses.
|
||||
``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of
|
||||
the template source and reload it
|
||||
automatically. By default the value is
|
||||
|
|
@ -239,7 +255,7 @@ The following configuration values are used internally by Flask:
|
|||
.. versionadded:: 0.10
|
||||
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_PRETTYPRINT_REGULAR``
|
||||
|
||||
.. versionadded:: 1.0
|
||||
.. versionadded:: 0.11
|
||||
``SESSION_REFRESH_EACH_REQUEST``, ``TEMPLATES_AUTO_RELOAD``,
|
||||
``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING``
|
||||
|
||||
|
|
@ -260,7 +276,7 @@ So a common pattern is this::
|
|||
|
||||
This first loads the configuration from the
|
||||
`yourapplication.default_settings` module and then overrides the values
|
||||
with the contents of the file the :envvar:``YOURAPPLICATION_SETTINGS``
|
||||
with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
|
||||
environment variable points to. This environment variable can be set on
|
||||
Linux or OS X with the export command in the shell before starting the
|
||||
server::
|
||||
|
|
@ -308,6 +324,7 @@ that experience:
|
|||
limit yourself to request-only accesses to the configuration you can
|
||||
reconfigure the object later on as needed.
|
||||
|
||||
.. _config-dev-prod:
|
||||
|
||||
Development / Production
|
||||
------------------------
|
||||
|
|
@ -329,7 +346,7 @@ there are alternative ways as well. For example you could use imports or
|
|||
subclassing.
|
||||
|
||||
What is very popular in the Django world is to make the import explicit in
|
||||
the config file by adding an ``from yourapplication.default_settings
|
||||
the config file by adding ``from yourapplication.default_settings
|
||||
import *`` to the top of the file and then overriding the changes by hand.
|
||||
You could also inspect an environment variable like
|
||||
``YOURAPPLICATION_MODE`` and set that to `production`, `development` etc
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ A basic FastCGI configuration for lighttpd looks like that::
|
|||
)
|
||||
|
||||
alias.url = (
|
||||
"/static/" => "/path/to/your/static"
|
||||
"/static/" => "/path/to/your/static/"
|
||||
)
|
||||
|
||||
url.rewrite-once = (
|
||||
|
|
@ -159,7 +159,7 @@ work in the URL root you have to work around a lighttpd bug with the
|
|||
|
||||
Make sure to apply it only if you are mounting the application the URL
|
||||
root. Also, see the Lighty docs for more information on `FastCGI and Python
|
||||
<http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
|
||||
<https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
|
||||
explicitly passing a socket to run() is no longer necessary).
|
||||
|
||||
Configuring nginx
|
||||
|
|
@ -234,7 +234,7 @@ python path. Common problems are:
|
|||
web server.
|
||||
- Different python interpreters being used.
|
||||
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _flup: https://pypi.python.org/pypi/flup
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ Hosted options
|
|||
|
||||
- `Deploying Flask on Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
|
||||
- `Deploying Flask on OpenShift <https://developers.openshift.com/en/python-flask.html>`_
|
||||
- `Deploying WSGI on dotCloud <http://docs.dotcloud.com/services/python/>`_
|
||||
with `Flask-specific notes <http://flask.pocoo.org/snippets/48/>`_
|
||||
- `Deploying Flask on Webfaction <http://flask.pocoo.org/snippets/65/>`_
|
||||
- `Deploying Flask on Google App Engine <https://github.com/kamalgill/flask-appengine-template>`_
|
||||
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
||||
- `Sharing your Localhost Server with Localtunnel <http://flask.pocoo.org/snippets/89/>`_
|
||||
|
||||
- `Deploying on Azure (IIS) <https://azure.microsoft.com/documentation/articles/web-sites-python-configure/>`_
|
||||
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
||||
|
||||
Self-hosted options
|
||||
-------------------
|
||||
|
|
@ -32,8 +32,8 @@ Self-hosted options
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
mod_wsgi
|
||||
wsgi-standalone
|
||||
uwsgi
|
||||
mod_wsgi
|
||||
fastcgi
|
||||
cgi
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
|
|||
not called because this will always start a local WSGI server which
|
||||
we do not want if we deploy that application to mod_wsgi.
|
||||
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
.. _Apache: https://httpd.apache.org/
|
||||
|
||||
Installing `mod_wsgi`
|
||||
---------------------
|
||||
|
|
@ -114,14 +114,28 @@ refuse to run with the above configuration. On a Windows system, eliminate those
|
|||
|
||||
Note: There have been some changes in access control configuration for `Apache 2.4`_.
|
||||
|
||||
.. _Apache 2.4: http://httpd.apache.org/docs/trunk/upgrading.html
|
||||
.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
|
||||
|
||||
For more information consult the `mod_wsgi wiki`_.
|
||||
Most notably, the syntax for directory permissions has changed from httpd 2.2
|
||||
|
||||
.. _mod_wsgi: http://code.google.com/p/modwsgi/
|
||||
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
|
||||
.. sourcecode:: apache
|
||||
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
|
||||
to httpd 2.4 syntax
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
Require all granted
|
||||
|
||||
|
||||
For more information consult the `mod_wsgi documentation`_.
|
||||
|
||||
.. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
|
||||
.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
|
||||
.. _virtual python: https://pypi.python.org/pypi/virtualenv
|
||||
.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/w/list
|
||||
.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
|
@ -129,7 +143,7 @@ Troubleshooting
|
|||
If your application does not run, follow this guide to troubleshoot:
|
||||
|
||||
**Problem:** application does not run, errorlog shows SystemExit ignored
|
||||
You have a ``app.run()`` call in your application file that is not
|
||||
You have an ``app.run()`` call in your application file that is not
|
||||
guarded by an ``if __name__ == '__main__':`` condition. Either
|
||||
remove that :meth:`~flask.Flask.run` call from the file and move it
|
||||
into a separate :file:`run.py` file or put it into such an if block.
|
||||
|
|
@ -193,5 +207,11 @@ Add the following lines to the top of your ``.wsgi`` file::
|
|||
activate_this = '/path/to/env/bin/activate_this.py'
|
||||
execfile(activate_this, dict(__file__=activate_this))
|
||||
|
||||
For Python 3 add the following lines to the top of your ``.wsgi`` file::
|
||||
|
||||
activate_this = '/path/to/env/bin/activate_this.py'
|
||||
with open(activate_this) as file_:
|
||||
exec(file_.read(), dict(__file__=activate_this))
|
||||
|
||||
This sets up the load paths according to the settings of the virtual
|
||||
environment. Keep in mind that the path has to be absolute.
|
||||
|
|
|
|||
|
|
@ -29,39 +29,44 @@ Given a flask application in myapp.py, use the following command:
|
|||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
|
||||
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app
|
||||
|
||||
Or, if you prefer:
|
||||
The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME`` to uwsgi,
|
||||
since its smarter about that. It is used together with the ``--mount`` directive
|
||||
which will make requests to ``/yourapplication`` be directed to ``myapp:app``.
|
||||
If your application is accessible at root level, you can use a single ``/``
|
||||
instead of ``/yourapplication``. ``myapp`` refers to the name of the file of
|
||||
your flask application (without extension) or the module which provides ``app``.
|
||||
``app`` is the callable inside of your application (usually the line reads
|
||||
``app = Flask(__name__)``.
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ uwsgi -s /tmp/uwsgi.sock -w myapp:app
|
||||
If you want to deploy your flask application inside of a virtual environment,
|
||||
you need to also add ``--virtualenv /path/to/virtual/environment``. You might
|
||||
also need to add ``--plugin python`` or ``--plugin python3`` depending on which
|
||||
python version you use for your project.
|
||||
|
||||
Configuring nginx
|
||||
-----------------
|
||||
|
||||
A basic flask uWSGI configuration for nginx looks like this::
|
||||
A basic flask nginx configuration looks like this::
|
||||
|
||||
location = /yourapplication { rewrite ^ /yourapplication/; }
|
||||
location /yourapplication { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include uwsgi_params;
|
||||
uwsgi_param SCRIPT_NAME /yourapplication;
|
||||
uwsgi_modifier1 30;
|
||||
uwsgi_pass unix:/tmp/uwsgi.sock;
|
||||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
||||
}
|
||||
|
||||
This configuration binds the application to ``/yourapplication``. If you want
|
||||
to have it in the URL root it's a bit simpler because you don't have to tell
|
||||
it the WSGI ``SCRIPT_NAME`` or set the uwsgi modifier to make use of it::
|
||||
to have it in the URL root its a bit simpler::
|
||||
|
||||
location / { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include uwsgi_params;
|
||||
uwsgi_pass unix:/tmp/uwsgi.sock;
|
||||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
||||
}
|
||||
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _uwsgi: http://projects.unbit.it/uwsgi/
|
||||
|
|
|
|||
|
|
@ -25,13 +25,29 @@ For example, to run a Flask application with 4 worker processes (``-w
|
|||
|
||||
.. _Gunicorn: http://gunicorn.org/
|
||||
.. _eventlet: http://eventlet.net/
|
||||
.. _greenlet: http://greenlet.readthedocs.org/en/latest/
|
||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||
|
||||
uWSGI
|
||||
--------
|
||||
|
||||
`uWSGI`_ is a fast application server written in C. It is very configurable
|
||||
which makes it more complicated to setup than gunicorn.
|
||||
|
||||
Running `uWSGI HTTP Router`_::
|
||||
|
||||
uwsgi --http 127.0.0.1:5000 --module myproject:app
|
||||
|
||||
For a more optimized setup, see `configuring uWSGI and NGINX`_.
|
||||
|
||||
.. _uWSGI: http://uwsgi-docs.readthedocs.io/en/latest/
|
||||
.. _uWSGI HTTP Router: http://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router
|
||||
.. _configuring uWSGI and NGINX: uwsgi.html#starting-your-app-with-uwsgi
|
||||
|
||||
Gevent
|
||||
-------
|
||||
|
||||
`Gevent`_ is a coroutine-based Python networking library that uses
|
||||
`greenlet`_ to provide a high-level synchronous API on top of `libevent`_
|
||||
`greenlet`_ to provide a high-level synchronous API on top of `libev`_
|
||||
event loop::
|
||||
|
||||
from gevent.wsgi import WSGIServer
|
||||
|
|
@ -41,8 +57,8 @@ event loop::
|
|||
http_server.serve_forever()
|
||||
|
||||
.. _Gevent: http://www.gevent.org/
|
||||
.. _greenlet: http://greenlet.readthedocs.org/en/latest/
|
||||
.. _libevent: http://libevent.org/
|
||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||
.. _libev: http://software.schmorp.de/pkg/libev.html
|
||||
|
||||
Twisted Web
|
||||
-----------
|
||||
|
|
@ -97,9 +113,10 @@ localhost at port 8000, setting appropriate headers:
|
|||
proxy_pass http://127.0.0.1:8000/;
|
||||
proxy_redirect off;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,46 @@ exception to the :attr:`~flask.Flask.logger`.
|
|||
But there is more you can do, and we will cover some better setups to deal
|
||||
with errors.
|
||||
|
||||
Error Logging Tools
|
||||
-------------------
|
||||
|
||||
Sending error mails, even if just for critical ones, can become
|
||||
overwhelming if enough users are hitting the error and log files are
|
||||
typically never looked at. This is why we recommend using `Sentry
|
||||
<https://www.getsentry.com/>`_ for dealing with application errors. It's
|
||||
available as an Open Source project `on GitHub
|
||||
<https://github.com/getsentry/sentry>`__ and is also available as a `hosted version
|
||||
<https://getsentry.com/signup/>`_ which you can try for free. Sentry
|
||||
aggregates duplicate errors, captures the full stack trace and local
|
||||
variables for debugging, and sends you mails based on new errors or
|
||||
frequency thresholds.
|
||||
|
||||
To use Sentry you need to install the `raven` client::
|
||||
|
||||
$ pip install raven
|
||||
|
||||
And then add this to your Flask app::
|
||||
|
||||
from raven.contrib.flask import Sentry
|
||||
sentry = Sentry(app, dsn='YOUR_DSN_HERE')
|
||||
|
||||
Or if you are using factories you can also init it later::
|
||||
|
||||
from raven.contrib.flask import Sentry
|
||||
sentry = Sentry(dsn='YOUR_DSN_HERE')
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
sentry.init_app(app)
|
||||
...
|
||||
return app
|
||||
|
||||
The `YOUR_DSN_HERE` value needs to be replaced with the DSN value you get
|
||||
from your Sentry installation.
|
||||
|
||||
Afterwards failures are automatically reported to Sentry and from there
|
||||
you can receive error notifications.
|
||||
|
||||
.. _error-handlers:
|
||||
|
||||
Error handlers
|
||||
|
|
@ -37,7 +77,7 @@ You might want to show custom error pages to the user when an error occurs.
|
|||
This can be done by registering error handlers.
|
||||
|
||||
Error handlers are normal :ref:`views` but instead of being registered for
|
||||
routes they are registered for exceptions that are rised while trying to
|
||||
routes, they are registered for exceptions that are raised while trying to
|
||||
do something else.
|
||||
|
||||
Registering
|
||||
|
|
@ -49,23 +89,24 @@ Register error handlers using :meth:`~flask.Flask.errorhandler` or
|
|||
@app.errorhandler(werkzeug.exceptions.BadRequest)
|
||||
def handle_bad_request(e):
|
||||
return 'bad request!'
|
||||
|
||||
|
||||
app.register_error_handler(400, lambda e: 'bad request!')
|
||||
|
||||
Those two ways are equivalent, but the first one is more clear and leaves
|
||||
you with a function to call on your whim (and in tests). Note that
|
||||
:exc:`werkzeug.exceptions.HTTPException` subclasses like
|
||||
:exc:`~werkzeug.exceptions.BadRequest` from the example and their HTTP codes
|
||||
are interchangable when handed to the registration methods or decorator
|
||||
are interchangeable when handed to the registration methods or decorator
|
||||
(``BadRequest.code == 400``).
|
||||
|
||||
You are however not limited to a :exc:`~werkzeug.exceptions.HTTPException`
|
||||
or its code but can register a handler for every exception class you like.
|
||||
You are however not limited to :exc:`~werkzeug.exceptions.HTTPException`
|
||||
or HTTP status codes but can register a handler for every exception class you
|
||||
like.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
.. versionchanged:: 0.11
|
||||
|
||||
Errorhandlers are now prioritized by specifity instead of the order they're
|
||||
registered in.
|
||||
Errorhandlers are now prioritized by specificity of the exception classes
|
||||
they are registered for instead of the order they are registered in.
|
||||
|
||||
Handling
|
||||
````````
|
||||
|
|
@ -74,7 +115,7 @@ Once an exception instance is raised, its class hierarchy is traversed,
|
|||
and searched for in the exception classes for which handlers are registered.
|
||||
The most specific handler is selected.
|
||||
|
||||
E.g. if a instance of :exc:`ConnectionRefusedError` is raised, and a handler
|
||||
E.g. if an instance of :exc:`ConnectionRefusedError` is raised, and a handler
|
||||
is registered for :exc:`ConnectionError` and :exc:`ConnectionRefusedError`,
|
||||
the more specific :exc:`ConnectionRefusedError` handler is called on the
|
||||
exception instance, and its response is shown to the user.
|
||||
|
|
@ -130,7 +171,7 @@ Logging to a File
|
|||
|
||||
Even if you get mails, you probably also want to log warnings. It's a
|
||||
good idea to keep as much information around that might be required to
|
||||
debug a problem. By default as of Flask 1.0, errors are logged to your
|
||||
debug a problem. By default as of Flask 0.11, errors are logged to your
|
||||
webserver's log automatically. Warnings however are not. Please note
|
||||
that Flask itself will not issue any warnings in the core system, so it's
|
||||
your responsibility to warn in the code if something seems odd.
|
||||
|
|
@ -175,7 +216,7 @@ A formatter can be instantiated with a format string. Note that
|
|||
tracebacks are appended to the log entry automatically. You don't have to
|
||||
do that in the log formatter format string.
|
||||
|
||||
Here some example setups:
|
||||
Here are some example setups:
|
||||
|
||||
Email
|
||||
`````
|
||||
|
|
@ -235,8 +276,9 @@ that this list is not complete, consult the official documentation of the
|
|||
| ``%(lineno)d`` | Source line number where the logging call was |
|
||||
| | issued (if available). |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(asctime)s`` | Human-readable time when the LogRecord` was |
|
||||
| | created. By default this is of the form |
|
||||
| ``%(asctime)s`` | Human-readable time when the |
|
||||
| | :class:`~logging.LogRecord` was created. |
|
||||
| | By default this is of the form |
|
||||
| | ``"2003-07-08 16:49:45,896"`` (the numbers after |
|
||||
| | the comma are millisecond portion of the time). |
|
||||
| | This can be changed by subclassing the formatter |
|
||||
|
|
|
|||
|
|
@ -29,13 +29,7 @@ be something like "Flask-SimpleXML". Make sure to include the name
|
|||
This is how users can then register dependencies to your extension in
|
||||
their :file:`setup.py` files.
|
||||
|
||||
Flask sets up a redirect package called :data:`flask.ext` where users
|
||||
should import the extensions from. If you for instance have a package
|
||||
called ``flask_something`` users would import it as
|
||||
``flask.ext.something``. This is done to transition from the old
|
||||
namespace packages. See :ref:`ext-import-transition` for more details.
|
||||
|
||||
But how do extensions look like themselves? An extension has to ensure
|
||||
But what do extensions look like themselves? An extension has to ensure
|
||||
that it works with multiple Flask application instances at once. This is
|
||||
a requirement because many people will use patterns like the
|
||||
:ref:`app-factories` pattern to create their application as needed to aid
|
||||
|
|
@ -355,60 +349,62 @@ new releases. These approved extensions are listed on the `Flask
|
|||
Extension Registry`_ and marked appropriately. If you want your own
|
||||
extension to be approved you have to follow these guidelines:
|
||||
|
||||
0. An approved Flask extension requires a maintainer. In the event an
|
||||
extension author would like to move beyond the project, the project should
|
||||
find a new maintainer including full source hosting transition and PyPI
|
||||
access. If no maintainer is available, give access to the Flask core team.
|
||||
1. An approved Flask extension must provide exactly one package or module
|
||||
named ``flask_extensionname``.
|
||||
2. It must ship a testing suite that can either be invoked with ``make test``
|
||||
or ``python setup.py test``. For test suites invoked with ``make
|
||||
test`` the extension has to ensure that all dependencies for the test
|
||||
are installed automatically. If tests are invoked with ``python setup.py
|
||||
test``, test dependencies can be specified in the `setup.py` file. The
|
||||
test suite also has to be part of the distribution.
|
||||
3. APIs of approved extensions will be checked for the following
|
||||
characteristics:
|
||||
0. An approved Flask extension requires a maintainer. In the event an
|
||||
extension author would like to move beyond the project, the project should
|
||||
find a new maintainer including full source hosting transition and PyPI
|
||||
access. If no maintainer is available, give access to the Flask core team.
|
||||
1. An approved Flask extension must provide exactly one package or module
|
||||
named ``flask_extensionname``.
|
||||
2. It must ship a testing suite that can either be invoked with ``make test``
|
||||
or ``python setup.py test``. For test suites invoked with ``make
|
||||
test`` the extension has to ensure that all dependencies for the test
|
||||
are installed automatically. If tests are invoked with ``python setup.py
|
||||
test``, test dependencies can be specified in the :file:`setup.py` file. The
|
||||
test suite also has to be part of the distribution.
|
||||
3. APIs of approved extensions will be checked for the following
|
||||
characteristics:
|
||||
|
||||
- an approved extension has to support multiple applications
|
||||
running in the same Python process.
|
||||
- it must be possible to use the factory pattern for creating
|
||||
applications.
|
||||
|
||||
4. The extension must be BSD/MIT/WTFPL licensed.
|
||||
5. The naming scheme for official extensions is *Flask-ExtensionName* or
|
||||
*ExtensionName-Flask*.
|
||||
6. Approved extensions must define all their dependencies in the
|
||||
`setup.py` file unless a dependency cannot be met because it is not
|
||||
available on PyPI.
|
||||
7. The extension must have documentation that uses one of the two Flask
|
||||
themes for Sphinx documentation.
|
||||
8. The ``zip_safe`` flag in the setup script must be set to ``False``,
|
||||
4. The license must be BSD/MIT/WTFPL licensed.
|
||||
5. The naming scheme for official extensions is *Flask-ExtensionName* or
|
||||
*ExtensionName-Flask*.
|
||||
6. Approved extensions must define all their dependencies in the
|
||||
:file:`setup.py` file unless a dependency cannot be met because it is not
|
||||
available on PyPI.
|
||||
7. The extension must have documentation that uses one of the two Flask
|
||||
themes for Sphinx documentation.
|
||||
8. The setup.py description (and thus the PyPI description) has to
|
||||
link to the documentation, website (if there is one) and there
|
||||
must be a link to automatically install the development version
|
||||
(``PackageName==dev``).
|
||||
9. The ``zip_safe`` flag in the setup script must be set to ``False``,
|
||||
even if the extension would be safe for zipping.
|
||||
9. An extension currently has to support Python 2.7, Python 3.3 and higher.
|
||||
10. An extension currently has to support Python 2.7, Python 3.3 and higher.
|
||||
|
||||
|
||||
.. _ext-import-transition:
|
||||
|
||||
Extension Import Transition
|
||||
---------------------------
|
||||
|
||||
For a while we recommended using namespace packages for Flask extensions.
|
||||
This turned out to be problematic in practice because many different
|
||||
competing namespace package systems exist and pip would automatically
|
||||
switch between different systems and this caused a lot of problems for
|
||||
users.
|
||||
In early versions of Flask we recommended using namespace packages for Flask
|
||||
extensions, of the form ``flaskext.foo``. This turned out to be problematic in
|
||||
practice because it meant that multiple ``flaskext`` packages coexist.
|
||||
Consequently we have recommended to name extensions ``flask_foo`` over
|
||||
``flaskext.foo`` for a long time.
|
||||
|
||||
Instead we now recommend naming packages ``flask_foo`` instead of the now
|
||||
deprecated ``flaskext.foo``. Flask 0.8 introduces a redirect import
|
||||
system that lets uses import from ``flask.ext.foo`` and it will try
|
||||
``flask_foo`` first and if that fails ``flaskext.foo``.
|
||||
Flask 0.8 introduced a redirect import system as a compatibility aid for app
|
||||
developers: Importing ``flask.ext.foo`` would try ``flask_foo`` and
|
||||
``flaskext.foo`` in that order.
|
||||
|
||||
Flask extensions should urge users to import from ``flask_foo``
|
||||
instead of ``flask.ext.foo`` or ``flaskext_foo`` so that extensions can
|
||||
transition to the new package name without affecting users.
|
||||
As of Flask 0.11, most Flask extensions have transitioned to the new naming
|
||||
schema. The ``flask.ext.foo`` compatibility alias is still in Flask 0.11 but is
|
||||
now deprecated -- you should use ``flask_foo``.
|
||||
|
||||
|
||||
.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/
|
||||
.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/
|
||||
.. _mailinglist: http://flask.pocoo.org/mailinglist/
|
||||
.. _IRC channel: http://flask.pocoo.org/community/irc/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _extensions:
|
||||
|
||||
Flask Extensions
|
||||
================
|
||||
|
||||
|
|
@ -18,10 +20,17 @@ Using Extensions
|
|||
Extensions typically have documentation that goes along that shows how to
|
||||
use it. There are no general rules in how extensions are supposed to
|
||||
behave but they are imported from common locations. If you have an
|
||||
extension called ``Flask-Foo`` or ``Foo-Flask`` it will be always
|
||||
importable from ``flask.ext.foo``::
|
||||
extension called ``Flask-Foo`` or ``Foo-Flask`` it should be always
|
||||
importable from ``flask_foo``::
|
||||
|
||||
from flask.ext import foo
|
||||
import flask_foo
|
||||
|
||||
Building Extensions
|
||||
-------------------
|
||||
|
||||
While `Flask Extension Registry`_ contains many Flask extensions, you may not find
|
||||
an extension that fits your need. If this is the case, you can always create your own.
|
||||
Consider reading :ref:`extension-dev` to develop your own Flask extension.
|
||||
|
||||
Flask Before 0.8
|
||||
----------------
|
||||
|
|
@ -44,5 +53,6 @@ And here is how you can use it::
|
|||
Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will
|
||||
exist and you can start importing from there.
|
||||
|
||||
|
||||
.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
|
||||
.. _flaskext_compat.py: https://raw.githubusercontent.com/mitsuhiko/flask/master/scripts/flaskext_compat.py
|
||||
.. _flaskext_compat.py: https://raw.githubusercontent.com/pallets/flask/master/scripts/flaskext_compat.py
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ the (X)HTML generation on the web is based on non-XML template engines
|
|||
(such as Jinja, the one used in Flask) which do not protect you from
|
||||
accidentally creating invalid XHTML. There are XML based template engines,
|
||||
such as Kid and the popular Genshi, but they often come with a larger
|
||||
runtime overhead and, are not as straightforward to use because they have
|
||||
runtime overhead and are not as straightforward to use because they have
|
||||
to obey XML rules.
|
||||
|
||||
The majority of users, however, assumed they were properly using XHTML.
|
||||
|
|
|
|||
|
|
@ -3,174 +3,173 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
Flask depends on some external libraries, like `Werkzeug
|
||||
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/>`_.
|
||||
Werkzeug is a toolkit for WSGI, the standard Python interface between web
|
||||
applications and a variety of servers for both development and deployment.
|
||||
Jinja2 renders templates.
|
||||
Python Version
|
||||
--------------
|
||||
|
||||
So how do you get all that on your computer quickly? There are many ways you
|
||||
could do that, but the most kick-ass method is virtualenv, so let's have a look
|
||||
at that first.
|
||||
We recommend using the latest version of Python 3. Flask supports Python 3.3
|
||||
and newer, Python 2.6 and newer, and PyPy.
|
||||
|
||||
You will need Python 2.6 or newer to get started, so be sure to have an
|
||||
up-to-date Python 2.x installation. For using Flask with Python 3 have a
|
||||
look at :ref:`python3-support`.
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
.. _virtualenv:
|
||||
These distributions will be installed automatically when installing Flask.
|
||||
|
||||
virtualenv
|
||||
----------
|
||||
* `Werkzeug`_ implements WSGI, the standard Python interface between
|
||||
applications and servers.
|
||||
* `Jinja`_ is a template language that renders the pages your application
|
||||
serves.
|
||||
* `MarkupSafe`_ comes with Jinja. It escapes untrusted input when rendering
|
||||
templates to avoid injection attacks.
|
||||
* `ItsDangerous`_ securely signs data to ensure its integrity. This is used
|
||||
to protect Flask's session cookie.
|
||||
* `Click`_ is a framework for writing command line applications. It provides
|
||||
the ``flask`` command and allows adding custom management commands.
|
||||
|
||||
Virtualenv is probably what you want to use during development, and if you have
|
||||
shell access to your production machines, you'll probably want to use it there,
|
||||
too.
|
||||
.. _Werkzeug: http://werkzeug.pocoo.org/
|
||||
.. _Jinja: http://jinja.pocoo.org/
|
||||
.. _MarkupSafe: https://pypi.python.org/pypi/MarkupSafe
|
||||
.. _ItsDangerous: https://pythonhosted.org/itsdangerous/
|
||||
.. _Click: http://click.pocoo.org/
|
||||
|
||||
What problem does virtualenv solve? If you like Python as much as I do,
|
||||
chances are you want to use it for other projects besides Flask-based web
|
||||
applications. But the more projects you have, the more likely it is that you
|
||||
will be working with different versions of Python itself, or at least different
|
||||
versions of Python libraries. Let's face it: quite often libraries break
|
||||
backwards compatibility, and it's unlikely that any serious application will
|
||||
have zero dependencies. So what do you do if two or more of your projects have
|
||||
conflicting dependencies?
|
||||
Optional dependencies
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Virtualenv to the rescue! Virtualenv enables multiple side-by-side
|
||||
installations of Python, one for each project. It doesn't actually install
|
||||
separate copies of Python, but it does provide a clever way to keep different
|
||||
project environments isolated. Let's see how virtualenv works.
|
||||
These distributions will not be installed automatically. Flask will detect and
|
||||
use them if you install them.
|
||||
|
||||
If you are on Mac OS X or Linux, chances are that one of the following two
|
||||
commands will work for you::
|
||||
* `Blinker`_ provides support for :ref:`signals`.
|
||||
* `SimpleJSON`_ is a fast JSON implementation that is compatible with
|
||||
Python's ``json`` module. It is preferred for JSON operations if it is
|
||||
installed.
|
||||
|
||||
$ sudo easy_install virtualenv
|
||||
.. _Blinker: https://pythonhosted.org/blinker/
|
||||
.. _SimpleJSON: https://simplejson.readthedocs.io/
|
||||
|
||||
or even better::
|
||||
Virtual environments
|
||||
--------------------
|
||||
|
||||
$ sudo pip install virtualenv
|
||||
Use a virtual environment to manage the dependencies for your project, both in
|
||||
development and in production.
|
||||
|
||||
One of these will probably install virtualenv on your system. Maybe it's even
|
||||
in your package manager. If you use Ubuntu, try::
|
||||
What problem does a virtual environment solve? The more Python projects you
|
||||
have, the more likely it is that you need to work with different versions of
|
||||
Python libraries, or even Python itself. Newer versions of libraries for one
|
||||
project can break compatibility in another project.
|
||||
|
||||
$ sudo apt-get install python-virtualenv
|
||||
Virtual environments are independent groups of Python libraries, one for each
|
||||
project. Packages installed for one project will not affect other projects or
|
||||
the operating system's packages.
|
||||
|
||||
If you are on Windows and don't have the :command:`easy_install` command, you must
|
||||
install it first. Check the :ref:`windows-easy-install` section for more
|
||||
information about how to do that. Once you have it installed, run the same
|
||||
commands as above, but without the :command:`sudo` prefix.
|
||||
Python 3 comes bundled with the :mod:`venv` module to create virtual
|
||||
environments. If you're using a modern version of Python, you can continue on
|
||||
to the next section.
|
||||
|
||||
Once you have virtualenv installed, just fire up a shell and create
|
||||
your own environment. I usually create a project folder and a :file:`venv`
|
||||
folder within::
|
||||
If you're using Python 2, see :ref:`install-install-virtualenv` first.
|
||||
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ virtualenv venv
|
||||
New python executable in venv/bin/python
|
||||
Installing setuptools, pip............done.
|
||||
.. _install-create-env:
|
||||
|
||||
Now, whenever you want to work on a project, you only have to activate the
|
||||
corresponding environment. On OS X and Linux, do the following::
|
||||
Create an environment
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
$ . venv/bin/activate
|
||||
Create a project folder and a :file:`venv` folder within:
|
||||
|
||||
If you are a Windows user, the following command is for you::
|
||||
.. code-block:: sh
|
||||
|
||||
$ venv\scripts\activate
|
||||
mkdir myproject
|
||||
cd myproject
|
||||
python3 -m venv venv
|
||||
|
||||
Either way, you should now be using your virtualenv (notice how the prompt of
|
||||
your shell has changed to show the active environment).
|
||||
On Windows:
|
||||
|
||||
And if you want to go back to the real world, use the following command::
|
||||
.. code-block:: bat
|
||||
|
||||
$ deactivate
|
||||
py -3 -m venv venv
|
||||
|
||||
After doing this, the prompt of your shell should be as familiar as before.
|
||||
If you needed to install virtualenv because you are on an older version of
|
||||
Python, use the following command instead:
|
||||
|
||||
Now, let's move on. Enter the following command to get Flask activated in your
|
||||
virtualenv::
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install Flask
|
||||
virtualenv venv
|
||||
|
||||
A few seconds later and you are good to go.
|
||||
On Windows:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
System-Wide Installation
|
||||
------------------------
|
||||
\Python27\Scripts\virtualenv.exe venv
|
||||
|
||||
This is possible as well, though I do not recommend it. Just run
|
||||
:command:`pip` with root privileges::
|
||||
Activate the environment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
$ sudo pip install Flask
|
||||
Before you work on your project, activate the corresponding environment:
|
||||
|
||||
(On Windows systems, run it in a command-prompt window with administrator
|
||||
privileges, and leave out :command:`sudo`.)
|
||||
.. code-block:: sh
|
||||
|
||||
. venv/bin/activate
|
||||
|
||||
Living on the Edge
|
||||
On Windows:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
venv\Scripts\activate
|
||||
|
||||
Your shell prompt will change to show the name of the activated environment.
|
||||
|
||||
Install Flask
|
||||
-------------
|
||||
|
||||
Within the activated environment, use the following command to install Flask:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install Flask
|
||||
|
||||
Living on the edge
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to work with the latest Flask code before it's released, install or
|
||||
update the code from the master branch:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install -U https://github.com/pallets/flask/archive/master.tar.gz
|
||||
|
||||
.. _install-install-virtualenv:
|
||||
|
||||
Install virtualenv
|
||||
------------------
|
||||
|
||||
If you want to work with the latest version of Flask, there are two ways: you
|
||||
can either let :command:`pip` pull in the development version, or you can tell
|
||||
it to operate on a git checkout. Either way, virtualenv is recommended.
|
||||
If you are using Python 2, the venv module is not available. Instead,
|
||||
install `virtualenv`_.
|
||||
|
||||
Get the git checkout in a new virtualenv and run in development mode::
|
||||
On Linux, virtualenv is provided by your package manager:
|
||||
|
||||
$ git clone http://github.com/mitsuhiko/flask.git
|
||||
Initialized empty Git repository in ~/dev/flask/.git/
|
||||
$ cd flask
|
||||
$ virtualenv venv
|
||||
New python executable in venv/bin/python
|
||||
Installing setuptools, pip............done.
|
||||
$ . venv/bin/activate
|
||||
$ python setup.py develop
|
||||
...
|
||||
Finished processing dependencies for Flask
|
||||
.. code-block:: sh
|
||||
|
||||
This will pull in the dependencies and activate the git head as the current
|
||||
version inside the virtualenv. Then all you have to do is run ``git pull
|
||||
origin`` to update to the latest version.
|
||||
# Debian, Ubuntu
|
||||
sudo apt-get install python-virtualenv
|
||||
|
||||
# CentOS, Fedora
|
||||
sudo yum install python-virtualenv
|
||||
|
||||
.. _windows-easy-install:
|
||||
# Arch
|
||||
sudo pacman -S python-virtualenv
|
||||
|
||||
`pip` and `setuptools` on Windows
|
||||
---------------------------------
|
||||
If you are on Mac OS X or Windows, download `get-pip.py`_, then:
|
||||
|
||||
Sometimes getting the standard "Python packaging tools" like *pip*, *setuptools*
|
||||
and *virtualenv* can be a little trickier, but nothing very hard. The two crucial
|
||||
packages you will need are setuptools and pip - these will let you install
|
||||
anything else (like virtualenv). Fortunately there are two "bootstrap scripts"
|
||||
you can run to install either.
|
||||
.. code-block:: sh
|
||||
|
||||
If you don't currently have either, then `get-pip.py` will install both for you
|
||||
(you won't need to run ez_setup.py).
|
||||
sudo python2 Downloads/get-pip.py
|
||||
sudo python2 -m pip install virtualenv
|
||||
|
||||
`get-pip.py`_
|
||||
On Windows, as an administrator:
|
||||
|
||||
To install the latest setuptools, you can use its bootstrap file:
|
||||
.. code-block:: bat
|
||||
|
||||
`ez_setup.py`_
|
||||
\Python27\python.exe Downloads\get-pip.py
|
||||
\Python27\python.exe -m pip install virtualenv
|
||||
|
||||
Either should be double-clickable once you download them. If you already have pip,
|
||||
you can upgrade them by running::
|
||||
Now you can continue to :ref:`install-create-env`.
|
||||
|
||||
> pip install --upgrade pip setuptools
|
||||
|
||||
Most often, once you pull up a command prompt you want to be able to type :command:`pip`
|
||||
and :command:`python` which will run those things, but this might not automatically happen
|
||||
on Windows, because it doesn't know where those executables are (give either a try!).
|
||||
|
||||
To fix this, you should be able to navigate to your Python install directory
|
||||
(e.g :file:`C:\Python27`), then go to :file:`Tools`, then :file:`Scripts`; then find the
|
||||
:file:`win_add2path.py` file and run that. Open a **new** Command Prompt and
|
||||
check that you can now just type :command:`python` to bring up the interpreter.
|
||||
|
||||
Finally, to install `virtualenv`_, you can simply run::
|
||||
|
||||
> pip install virtualenv
|
||||
|
||||
Then you can be off on your way following the installation instructions above.
|
||||
|
||||
.. _get-pip.py: https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
|
||||
.. _ez_setup.py: https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
|
||||
.. _virtualenv: https://virtualenv.pypa.io/
|
||||
.. _get-pip.py: https://bootstrap.pypa.io/get-pip.py
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Implementing API Exceptions
|
|||
===========================
|
||||
|
||||
It's very common to implement RESTful APIs on top of Flask. One of the
|
||||
first thing that developers run into is the realization that the builtin
|
||||
first things that developers run into is the realization that the builtin
|
||||
exceptions are not expressive enough for APIs and that the content type of
|
||||
:mimetype:`text/html` they are emitting is not very useful for API consumers.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ Application Dispatching
|
|||
=======================
|
||||
|
||||
Application dispatching is the process of combining multiple Flask
|
||||
applications on the WSGI level. You can not only combine Flask
|
||||
applications into something larger but any WSGI application. This would
|
||||
even allow you to run a Django and a Flask application in the same
|
||||
interpreter side by side if you want. The usefulness of this depends on
|
||||
how the applications work internally.
|
||||
applications on the WSGI level. You can combine not only Flask
|
||||
applications but any WSGI application. This would allow you to run a
|
||||
Django and a Flask application in the same interpreter side by side if
|
||||
you want. The usefulness of this depends on how the applications work
|
||||
internally.
|
||||
|
||||
The fundamental difference from the :ref:`module approach
|
||||
<larger-applications>` is that in this case you are running the same or
|
||||
|
|
@ -31,7 +31,7 @@ Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
|
|||
use in production. Use a :ref:`full-blown WSGI server <deployment>`.
|
||||
|
||||
In order to use the interactive debugger, debugging must be enabled both on
|
||||
the application and the simple server, here is the "hello world" example with
|
||||
the application and the simple server. Here is the "hello world" example with
|
||||
debugging and :func:`run_simple <werkzeug.serving.run_simple>`::
|
||||
|
||||
from flask import Flask
|
||||
|
|
@ -56,7 +56,7 @@ If you have entirely separated applications and you want them to work next
|
|||
to each other in the same Python interpreter process you can take
|
||||
advantage of the :class:`werkzeug.wsgi.DispatcherMiddleware`. The idea
|
||||
here is that each Flask application is a valid WSGI application and they
|
||||
are combined by the dispatcher middleware into a larger one that
|
||||
are combined by the dispatcher middleware into a larger one that is
|
||||
dispatched based on prefix.
|
||||
|
||||
For example you could have your main application run on ``/`` and your
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ Application Factories
|
|||
If you are already using packages and blueprints for your application
|
||||
(:ref:`blueprints`) there are a couple of really nice ways to further improve
|
||||
the experience. A common pattern is creating the application object when
|
||||
the blueprint is imported. But if you move the creation of this object,
|
||||
into a function, you can then create multiple instances of this and later.
|
||||
the blueprint is imported. But if you move the creation of this object
|
||||
into a function, you can then create multiple instances of this app later.
|
||||
|
||||
So why would you want to do this?
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ Factories & Extensions
|
|||
It's preferable to create your extensions and app factories so that the
|
||||
extension object does not initially get bound to the application.
|
||||
|
||||
Using `Flask-SQLAlchemy <http://pythonhosted.org/Flask-SQLAlchemy/>`_,
|
||||
Using `Flask-SQLAlchemy <http://flask-sqlalchemy.pocoo.org/>`_,
|
||||
as an example, you should not do something along those lines::
|
||||
|
||||
def create_app(config_filename):
|
||||
|
|
@ -99,7 +99,8 @@ an application::
|
|||
|
||||
It can then be used with the :command:`flask` command::
|
||||
|
||||
flask --app=exampleapp run
|
||||
export FLASK_APP=exampleapp
|
||||
flask run
|
||||
|
||||
Factory Improvements
|
||||
--------------------
|
||||
|
|
|
|||
|
|
@ -1,24 +1,27 @@
|
|||
Celery Based Background Tasks
|
||||
=============================
|
||||
Celery Background Tasks
|
||||
=======================
|
||||
|
||||
Celery is a task queue for Python with batteries included. It used to
|
||||
have a Flask integration but it became unnecessary after some
|
||||
restructuring of the internals of Celery with Version 3. This guide fills
|
||||
in the blanks in how to properly use Celery with Flask but assumes that
|
||||
you generally already read the `First Steps with Celery
|
||||
<http://docs.celeryproject.org/en/master/getting-started/first-steps-with-celery.html>`_
|
||||
guide in the official Celery documentation.
|
||||
If your application has a long running task, such as processing some uploaded
|
||||
data or sending email, you don't want to wait for it to finish during a
|
||||
request. Instead, use a task queue to send the necessary data to another
|
||||
process that will run the task in the background while the request returns
|
||||
immediately.
|
||||
|
||||
Installing Celery
|
||||
-----------------
|
||||
Celery is a powerful task queue that can be used for simple background tasks
|
||||
as well as complex multi-stage programs and schedules. This guide will show you
|
||||
how to configure Celery using Flask, but assumes you've already read the
|
||||
`First Steps with Celery <http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html>`_
|
||||
guide in the Celery documentation.
|
||||
|
||||
Celery is on the Python Package Index (PyPI), so it can be installed with
|
||||
standard Python tools like :command:`pip` or :command:`easy_install`::
|
||||
Install
|
||||
-------
|
||||
|
||||
Celery is a separate Python package. Install it from PyPI using pip::
|
||||
|
||||
$ pip install celery
|
||||
|
||||
Configuring Celery
|
||||
------------------
|
||||
Configure
|
||||
---------
|
||||
|
||||
The first thing you need is a Celery instance, this is called the celery
|
||||
application. It serves the same purpose as the :class:`~flask.Flask`
|
||||
|
|
@ -36,15 +39,18 @@ This is all that is necessary to properly integrate Celery with Flask::
|
|||
from celery import Celery
|
||||
|
||||
def make_celery(app):
|
||||
celery = Celery(app.import_name, backend=app.config['CELERY_BACKEND'],
|
||||
broker=app.config['CELERY_BROKER_URL'])
|
||||
celery = Celery(
|
||||
app.import_name,
|
||||
backend=app.config['CELERY_RESULT_BACKEND'],
|
||||
broker=app.config['CELERY_BROKER_URL']
|
||||
)
|
||||
celery.conf.update(app.config)
|
||||
TaskBase = celery.Task
|
||||
class ContextTask(TaskBase):
|
||||
abstract = True
|
||||
|
||||
class ContextTask(celery.Task):
|
||||
def __call__(self, *args, **kwargs):
|
||||
with app.app_context():
|
||||
return TaskBase.__call__(self, *args, **kwargs)
|
||||
return self.run(*args, **kwargs)
|
||||
|
||||
celery.Task = ContextTask
|
||||
return celery
|
||||
|
||||
|
|
@ -53,11 +59,12 @@ from the application config, updates the rest of the Celery config from
|
|||
the Flask config and then creates a subclass of the task that wraps the
|
||||
task execution in an application context.
|
||||
|
||||
Minimal Example
|
||||
An example task
|
||||
---------------
|
||||
|
||||
With what we have above this is the minimal example of using Celery with
|
||||
Flask::
|
||||
Let's write a task that adds two numbers together and returns the result. We
|
||||
configure Celery's broker and backend to use Redis, create a ``celery``
|
||||
application using the factor from above, and then use it to define the task. ::
|
||||
|
||||
from flask import Flask
|
||||
|
||||
|
|
@ -68,26 +75,27 @@ Flask::
|
|||
)
|
||||
celery = make_celery(flask_app)
|
||||
|
||||
|
||||
@celery.task()
|
||||
def add_together(a, b):
|
||||
return a + b
|
||||
|
||||
This task can now be called in the background:
|
||||
This task can now be called in the background::
|
||||
|
||||
>>> result = add_together.delay(23, 42)
|
||||
>>> result.wait()
|
||||
65
|
||||
result = add_together.delay(23, 42)
|
||||
result.wait() # 65
|
||||
|
||||
Running the Celery Worker
|
||||
-------------------------
|
||||
Run a worker
|
||||
------------
|
||||
|
||||
Now if you jumped in and already executed the above code you will be
|
||||
disappointed to learn that your ``.wait()`` will never actually return.
|
||||
That's because you also need to run celery. You can do that by running
|
||||
celery as a worker::
|
||||
If you jumped in and already executed the above code you will be
|
||||
disappointed to learn that ``.wait()`` will never actually return.
|
||||
That's because you also need to run a Celery worker to receive and execute the
|
||||
task. ::
|
||||
|
||||
$ celery -A your_application.celery worker
|
||||
|
||||
The ``your_application`` string has to point to your application's package
|
||||
or module that creates the `celery` object.
|
||||
or module that creates the ``celery`` object.
|
||||
|
||||
Now that the worker is running, ``wait`` will return the result once the task
|
||||
is finished.
|
||||
|
|
|
|||
|
|
@ -3,71 +3,43 @@
|
|||
Deferred Request Callbacks
|
||||
==========================
|
||||
|
||||
One of the design principles of Flask is that response objects are created
|
||||
and passed down a chain of potential callbacks that can modify them or
|
||||
replace them. When the request handling starts, there is no response
|
||||
object yet. It is created as necessary either by a view function or by
|
||||
some other component in the system.
|
||||
One of the design principles of Flask is that response objects are created and
|
||||
passed down a chain of potential callbacks that can modify them or replace
|
||||
them. When the request handling starts, there is no response object yet. It is
|
||||
created as necessary either by a view function or by some other component in
|
||||
the system.
|
||||
|
||||
But what happens if you want to modify the response at a point where the
|
||||
response does not exist yet? A common example for that would be a
|
||||
before-request function that wants to set a cookie on the response object.
|
||||
What happens if you want to modify the response at a point where the response
|
||||
does not exist yet? A common example for that would be a
|
||||
:meth:`~flask.Flask.before_request` callback that wants to set a cookie on the
|
||||
response object.
|
||||
|
||||
One way is to avoid the situation. Very often that is possible. For
|
||||
instance you can try to move that logic into an after-request callback
|
||||
instead. Sometimes however moving that code there is just not a very
|
||||
pleasant experience or makes code look very awkward.
|
||||
One way is to avoid the situation. Very often that is possible. For instance
|
||||
you can try to move that logic into a :meth:`~flask.Flask.after_request`
|
||||
callback instead. However, sometimes moving code there makes it more
|
||||
more complicated or awkward to reason about.
|
||||
|
||||
As an alternative possibility you can attach a bunch of callback functions
|
||||
to the :data:`~flask.g` object and call them at the end of the request.
|
||||
This way you can defer code execution from anywhere in the application.
|
||||
As an alternative, you can use :func:`~flask.after_this_request` to register
|
||||
callbacks that will execute after only the current request. This way you can
|
||||
defer code execution from anywhere in the application, based on the current
|
||||
request.
|
||||
|
||||
At any time during a request, we can register a function to be called at the
|
||||
end of the request. For example you can remember the current language of the
|
||||
user in a cookie in a :meth:`~flask.Flask.before_request` callback::
|
||||
|
||||
The Decorator
|
||||
-------------
|
||||
|
||||
The following decorator is the key. It registers a function on a list on
|
||||
the :data:`~flask.g` object::
|
||||
|
||||
from flask import g
|
||||
|
||||
def after_this_request(f):
|
||||
if not hasattr(g, 'after_request_callbacks'):
|
||||
g.after_request_callbacks = []
|
||||
g.after_request_callbacks.append(f)
|
||||
return f
|
||||
|
||||
|
||||
Calling the Deferred
|
||||
--------------------
|
||||
|
||||
Now you can use the `after_this_request` decorator to mark a function to
|
||||
be called at the end of the request. But we still need to call them. For
|
||||
this the following function needs to be registered as
|
||||
:meth:`~flask.Flask.after_request` callback::
|
||||
|
||||
@app.after_request
|
||||
def call_after_request_callbacks(response):
|
||||
for callback in getattr(g, 'after_request_callbacks', ()):
|
||||
callback(response)
|
||||
return response
|
||||
|
||||
|
||||
A Practical Example
|
||||
-------------------
|
||||
|
||||
Now we can easily at any point in time register a function to be called at
|
||||
the end of this particular request. For example you can remember the
|
||||
current language of the user in a cookie in the before-request function::
|
||||
|
||||
from flask import request
|
||||
from flask import request, after_this_request
|
||||
|
||||
@app.before_request
|
||||
def detect_user_language():
|
||||
language = request.cookies.get('user_lang')
|
||||
|
||||
if language is None:
|
||||
language = guess_language_from_request()
|
||||
|
||||
# when the response exists, set a cookie with the language
|
||||
@after_this_request
|
||||
def remember_language(response):
|
||||
response.set_cookie('user_lang', language)
|
||||
|
||||
g.language = language
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
.. _distribute-deployment:
|
||||
|
||||
Deploying with Distribute
|
||||
Deploying with Setuptools
|
||||
=========================
|
||||
|
||||
`distribute`_, formerly setuptools, is an extension library that is
|
||||
commonly used to (like the name says) distribute Python libraries and
|
||||
extensions. It extends distutils, a basic module installation system
|
||||
shipped with Python to also support various more complex constructs that
|
||||
make larger applications easier to distribute:
|
||||
`Setuptools`_, is an extension library that is commonly used to
|
||||
distribute Python libraries and extensions. It extends distutils, a basic
|
||||
module installation system shipped with Python to also support various more
|
||||
complex constructs that make larger applications easier to distribute:
|
||||
|
||||
- **support for dependencies**: a library or application can declare a
|
||||
list of other libraries it depends on which will be installed
|
||||
|
|
@ -16,34 +15,32 @@ make larger applications easier to distribute:
|
|||
Python installation. This makes it possible to query information
|
||||
provided by one package from another package. The best known feature of
|
||||
this system is the entry point support which allows one package to
|
||||
declare an "entry point" another package can hook into to extend the
|
||||
declare an "entry point" that another package can hook into to extend the
|
||||
other package.
|
||||
- **installation manager**: :command:`easy_install`, which comes with distribute
|
||||
can install other libraries for you. You can also use `pip`_ which
|
||||
sooner or later will replace :command:`easy_install` which does more than just
|
||||
installing packages for you.
|
||||
- **installation manager**: :command:`pip` can install other libraries for you.
|
||||
|
||||
Flask itself, and all the libraries you can find on the cheeseshop
|
||||
are distributed with either distribute, the older setuptools or distutils.
|
||||
If you have Python 2 (>=2.7.9) or Python 3 (>=3.4) installed from python.org,
|
||||
you will already have pip and setuptools on your system. Otherwise, you
|
||||
will need to install them yourself.
|
||||
|
||||
Flask itself, and all the libraries you can find on PyPI are distributed with
|
||||
either setuptools or distutils.
|
||||
|
||||
In this case we assume your application is called
|
||||
:file:`yourapplication.py` and you are not using a module, but a :ref:`package
|
||||
<larger-applications>`. Distributing resources with standard modules is
|
||||
not supported by `distribute`_ so we will not bother with it. If you have
|
||||
not yet converted your application into a package, head over to the
|
||||
:ref:`larger-applications` pattern to see how this can be done.
|
||||
<larger-applications>`. If you have not yet converted your application into
|
||||
a package, head over to the :ref:`larger-applications` pattern to see
|
||||
how this can be done.
|
||||
|
||||
A working deployment with distribute is the first step into more complex
|
||||
A working deployment with setuptools is the first step into more complex
|
||||
and more automated deployment scenarios. If you want to fully automate
|
||||
the process, also read the :ref:`fabric-deployment` chapter.
|
||||
|
||||
Basic Setup Script
|
||||
------------------
|
||||
|
||||
Because you have Flask running, you either have setuptools or distribute
|
||||
available on your system anyways. If you do not, fear not, there is a
|
||||
script to install it for you: `distribute_setup.py`_. Just download and
|
||||
run with your Python interpreter.
|
||||
Because you have Flask installed, you have setuptools available on your system.
|
||||
Flask already depends upon setuptools.
|
||||
|
||||
Standard disclaimer applies: :ref:`you better use a virtualenv
|
||||
<virtualenv>`.
|
||||
|
|
@ -52,10 +49,6 @@ Your setup code always goes into a file named :file:`setup.py` next to your
|
|||
application. The name of the file is only convention, but because
|
||||
everybody will look for a file with that name, you better not change it.
|
||||
|
||||
Yes, even if you are using `distribute`, you are importing from a package
|
||||
called `setuptools`. `distribute` is fully backwards compatible with
|
||||
`setuptools`, so it also uses the same import name.
|
||||
|
||||
A basic :file:`setup.py` file for a Flask application looks like this::
|
||||
|
||||
from setuptools import setup
|
||||
|
|
@ -71,8 +64,8 @@ A basic :file:`setup.py` file for a Flask application looks like this::
|
|||
)
|
||||
|
||||
Please keep in mind that you have to list subpackages explicitly. If you
|
||||
want distribute to lookup the packages for you automatically, you can use
|
||||
the `find_packages` function::
|
||||
want setuptools to lookup the packages for you automatically, you can use
|
||||
the ``find_packages`` function::
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
|
@ -81,17 +74,36 @@ the `find_packages` function::
|
|||
packages=find_packages()
|
||||
)
|
||||
|
||||
Most parameters to the `setup` function should be self explanatory,
|
||||
`include_package_data` and `zip_safe` might not be.
|
||||
`include_package_data` tells distribute to look for a :file:`MANIFEST.in` file
|
||||
Most parameters to the ``setup`` function should be self explanatory,
|
||||
``include_package_data`` and ``zip_safe`` might not be.
|
||||
``include_package_data`` tells setuptools to look for a :file:`MANIFEST.in` file
|
||||
and install all the entries that match as package data. We will use this
|
||||
to distribute the static files and templates along with the Python module
|
||||
(see :ref:`distributing-resources`). The `zip_safe` flag can be used to
|
||||
(see :ref:`distributing-resources`). The ``zip_safe`` flag can be used to
|
||||
force or prevent zip Archive creation. In general you probably don't want
|
||||
your packages to be installed as zip files because some tools do not
|
||||
support them and they make debugging a lot harder.
|
||||
|
||||
|
||||
Tagging Builds
|
||||
--------------
|
||||
|
||||
It is useful to distinguish between release and development builds. Add a
|
||||
:file:`setup.cfg` file to configure these options.
|
||||
|
||||
[egg_info]
|
||||
tag_build = .dev
|
||||
tag_date = 1
|
||||
|
||||
[aliases]
|
||||
release = egg_info -RDb ''
|
||||
|
||||
Running ``python setup.py sdist`` will create a development package
|
||||
with ".dev" and the current date appended: ``flaskr-1.0.dev20160314.tar.gz``.
|
||||
Running ``python setup.py release sdist`` will create a release package
|
||||
with only the version: ``flaskr-1.0.tar.gz``.
|
||||
|
||||
|
||||
.. _distributing-resources:
|
||||
|
||||
Distributing Resources
|
||||
|
|
@ -99,7 +111,7 @@ Distributing Resources
|
|||
|
||||
If you try to install the package you just created, you will notice that
|
||||
folders like :file:`static` or :file:`templates` are not installed for you. The
|
||||
reason for this is that distribute does not know which files to add for
|
||||
reason for this is that setuptools does not know which files to add for
|
||||
you. What you should do, is to create a :file:`MANIFEST.in` file next to your
|
||||
:file:`setup.py` file. This file lists all the files that should be added to
|
||||
your tarball::
|
||||
|
|
@ -109,13 +121,13 @@ your tarball::
|
|||
|
||||
Don't forget that even if you enlist them in your :file:`MANIFEST.in` file, they
|
||||
won't be installed for you unless you set the `include_package_data`
|
||||
parameter of the `setup` function to ``True``!
|
||||
parameter of the ``setup`` function to ``True``!
|
||||
|
||||
|
||||
Declaring Dependencies
|
||||
----------------------
|
||||
|
||||
Dependencies are declared in the `install_requires` parameter as list.
|
||||
Dependencies are declared in the ``install_requires`` parameter as a list.
|
||||
Each item in that list is the name of a package that should be pulled from
|
||||
PyPI on installation. By default it will always use the most recent
|
||||
version, but you can also provide minimum and maximum version
|
||||
|
|
@ -127,40 +139,39 @@ requirements. Here some examples::
|
|||
'BrokenPackage>=0.7,<=1.0'
|
||||
]
|
||||
|
||||
I mentioned earlier that dependencies are pulled from PyPI. What if you
|
||||
As mentioned earlier, dependencies are pulled from PyPI. What if you
|
||||
want to depend on a package that cannot be found on PyPI and won't be
|
||||
because it is an internal package you don't want to share with anyone?
|
||||
Just still do as if there was a PyPI entry for it and provide a list of
|
||||
alternative locations where distribute should look for tarballs::
|
||||
Just do it as if there was a PyPI entry and provide a list of
|
||||
alternative locations where setuptools should look for tarballs::
|
||||
|
||||
dependency_links=['http://example.com/yourfiles']
|
||||
|
||||
Make sure that page has a directory listing and the links on the page are
|
||||
pointing to the actual tarballs with their correct filenames as this is
|
||||
how distribute will find the files. If you have an internal company
|
||||
server that contains the packages, provide the URL to that server there.
|
||||
how setuptools will find the files. If you have an internal company
|
||||
server that contains the packages, provide the URL to that server.
|
||||
|
||||
|
||||
Installing / Developing
|
||||
-----------------------
|
||||
|
||||
To install your application (ideally into a virtualenv) just run the
|
||||
:file:`setup.py` script with the `install` parameter. It will install your
|
||||
:file:`setup.py` script with the ``install`` parameter. It will install your
|
||||
application into the virtualenv's site-packages folder and also download
|
||||
and install all dependencies::
|
||||
|
||||
$ python setup.py install
|
||||
|
||||
If you are developing on the package and also want the requirements to be
|
||||
installed, you can use the `develop` command instead::
|
||||
installed, you can use the ``develop`` command instead::
|
||||
|
||||
$ python setup.py develop
|
||||
|
||||
This has the advantage of just installing a link to the site-packages
|
||||
folder instead of copying the data over. You can then continue to work on
|
||||
the code without having to run `install` again after each change.
|
||||
the code without having to run ``install`` again after each change.
|
||||
|
||||
|
||||
.. _distribute: https://pypi.python.org/pypi/distribute
|
||||
.. _pip: https://pypi.python.org/pypi/pip
|
||||
.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py
|
||||
.. _Setuptools: https://pypi.python.org/pypi/setuptools
|
||||
|
|
|
|||
|
|
@ -47,37 +47,53 @@ even if the application behaves correctly:
|
|||
Error Handlers
|
||||
--------------
|
||||
|
||||
An error handler is a function, just like a view function, but it is
|
||||
called when an error happens and is passed that error. The error is most
|
||||
likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it
|
||||
can be a different error: a handler for internal server errors will be
|
||||
passed other exception instances as well if they are uncaught.
|
||||
An error handler is a function that returns a response when a type of error is
|
||||
raised, similar to how a view is a function that returns a response when a
|
||||
request URL is matched. It is passed the instance of the error being handled,
|
||||
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error
|
||||
handler for "500 Internal Server Error" will be passed uncaught exceptions in
|
||||
addition to explicit 500 errors.
|
||||
|
||||
An error handler is registered with the :meth:`~flask.Flask.errorhandler`
|
||||
decorator and the error code of the exception. Keep in mind that Flask
|
||||
will *not* set the error code for you, so make sure to also provide the
|
||||
HTTP status code when returning a response.
|
||||
decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler
|
||||
can be registered for a status code, like 404, or for an exception class.
|
||||
|
||||
Please note that if you add an error handler for "500 Internal Server
|
||||
Error", Flask will not trigger it if it's running in Debug mode.
|
||||
The status code of the response will not be set to the handler's code. Make
|
||||
sure to provide the appropriate HTTP status code when returning a response from
|
||||
a handler.
|
||||
|
||||
Here an example implementation for a "404 Page Not Found" exception::
|
||||
A handler for "500 Internal Server Error" will not be used when running in
|
||||
debug mode. Instead, the interactive debugger will be shown.
|
||||
|
||||
Here is an example implementation for a "404 Page Not Found" exception::
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# note that we set the 404 status explicitly
|
||||
return render_template('404.html'), 404
|
||||
|
||||
When using the :ref:`application factory pattern <app-factories>`::
|
||||
|
||||
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 %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Page Not Found{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -43,37 +43,26 @@ virtual environment::
|
|||
env.hosts = ['server1.example.com', 'server2.example.com']
|
||||
|
||||
def pack():
|
||||
# create a new source distribution as tarball
|
||||
# build the package
|
||||
local('python setup.py sdist --formats=gztar', capture=False)
|
||||
|
||||
def deploy():
|
||||
# figure out the release name and version
|
||||
# figure out the package name and version
|
||||
dist = local('python setup.py --fullname', capture=True).strip()
|
||||
# upload the source tarball to the temporary folder on the server
|
||||
put('dist/%s.tar.gz' % dist, '/tmp/yourapplication.tar.gz')
|
||||
# create a place where we can unzip the tarball, then enter
|
||||
# that directory and unzip it
|
||||
run('mkdir /tmp/yourapplication')
|
||||
with cd('/tmp/yourapplication'):
|
||||
run('tar xzf /tmp/yourapplication.tar.gz')
|
||||
# now setup the package with our virtual environment's
|
||||
# python interpreter
|
||||
run('/var/www/yourapplication/env/bin/python setup.py install')
|
||||
# now that all is set up, delete the folder again
|
||||
run('rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz')
|
||||
# and finally touch the .wsgi file so that mod_wsgi triggers
|
||||
# a reload of the application
|
||||
filename = '%s.tar.gz' % dist
|
||||
|
||||
# upload the package to the temporary folder on the server
|
||||
put('dist/%s' % filename, '/tmp/%s' % filename)
|
||||
|
||||
# install the package in the application's virtualenv with pip
|
||||
run('/var/www/yourapplication/env/bin/pip install /tmp/%s' % filename)
|
||||
|
||||
# remove the uploaded package
|
||||
run('rm -r /tmp/%s' % filename)
|
||||
|
||||
# touch the .wsgi file to trigger a reload in mod_wsgi
|
||||
run('touch /var/www/yourapplication.wsgi')
|
||||
|
||||
The example above is well documented and should be straightforward. Here
|
||||
a recap of the most common commands fabric provides:
|
||||
|
||||
- `run` - executes a command on a remote server
|
||||
- `local` - executes a command on the local machine
|
||||
- `put` - uploads a file to the remote server
|
||||
- `cd` - changes the directory on the serverside. This has to be used
|
||||
in combination with the ``with`` statement.
|
||||
|
||||
Running Fabfiles
|
||||
----------------
|
||||
|
||||
|
|
@ -156,6 +145,7 @@ location where it's expected (eg: :file:`/var/www/yourapplication`).
|
|||
Either way, in our case here we only expect one or two servers and we can
|
||||
upload them ahead of time by hand.
|
||||
|
||||
|
||||
First Deployment
|
||||
----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ web server's documentation.
|
|||
See also
|
||||
--------
|
||||
|
||||
* The `Favicon <http://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
* The `Favicon <https://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
Wikipedia
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
@ -40,14 +40,14 @@ your users to be able to upload everything there if the server is directly
|
|||
sending out the data to the client. That way you can make sure that users
|
||||
are not able to upload HTML files that would cause XSS problems (see
|
||||
:ref:`xss`). Also make sure to disallow ``.php`` files if the server
|
||||
executes them, but who has PHP installed on his server, right? :)
|
||||
executes them, but who has PHP installed on their server, right? :)
|
||||
|
||||
Next the functions that check if an extension is valid and that uploads
|
||||
the file and redirects the user to the URL for the uploaded file::
|
||||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
|
||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def upload_file():
|
||||
|
|
@ -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)
|
||||
|
|
@ -71,9 +71,9 @@ the file and redirects the user to the URL for the uploaded file::
|
|||
<!doctype html>
|
||||
<title>Upload new File</title>
|
||||
<h1>Upload new File</h1>
|
||||
<form action="" method=post enctype=multipart/form-data>
|
||||
<p><input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
<form method=post enctype=multipart/form-data>
|
||||
<input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
</form>
|
||||
'''
|
||||
|
||||
|
|
@ -104,9 +104,9 @@ before storing it directly on the filesystem.
|
|||
>>> secure_filename('../../../../home/username/.bashrc')
|
||||
'home_username_.bashrc'
|
||||
|
||||
Now one last thing is missing: the serving of the uploaded files. In the
|
||||
:func:`upload_file()` we redirect the user to
|
||||
``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
|
||||
Now one last thing is missing: the serving of the uploaded files. In the
|
||||
:func:`upload_file()` we redirect the user to
|
||||
``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
|
||||
So we write the :func:`uploaded_file` function to return the file of that name. As
|
||||
of Flask 0.5 we can use a function that does that for us::
|
||||
|
||||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ 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.
|
||||
template that does this. Note that browsers and sometimes web servers enforce
|
||||
a limit on cookie sizes. This means that flashing messages that are too
|
||||
large for session cookies causes message flashing to fail silently.
|
||||
|
||||
Simple Flashing
|
||||
---------------
|
||||
|
|
@ -76,7 +78,7 @@ And here is the :file:`login.html` template which also inherits from
|
|||
{% if error %}
|
||||
<p class=error><strong>Error:</strong> {{ error }}
|
||||
{% endif %}
|
||||
<form action="" method=post>
|
||||
<form method=post>
|
||||
<dl>
|
||||
<dt>Username:
|
||||
<dd><input type=text name=username value="{{
|
||||
|
|
|
|||
|
|
@ -41,3 +41,4 @@ Snippet Archives <http://flask.pocoo.org/snippets/>`_.
|
|||
methodoverrides
|
||||
requestchecksum
|
||||
celery
|
||||
subclassing
|
||||
|
|
|
|||
|
|
@ -164,5 +164,5 @@ explanation of the little bit of code above:
|
|||
|
||||
If you don't get the whole picture, download the `sourcecode
|
||||
for this example
|
||||
<https://github.com/mitsuhiko/flask/tree/master/examples/jqueryexample>`_
|
||||
<https://github.com/pallets/flask/tree/master/examples/jqueryexample>`_
|
||||
from GitHub.
|
||||
|
|
|
|||
|
|
@ -90,14 +90,19 @@ Then you can define your central place to combine the views like this::
|
|||
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::
|
||||
name and a dot, and by wrapping `view_func` in a `LazyView` as needed. ::
|
||||
|
||||
def url(url_rule, import_name, **options):
|
||||
def url(import_name, url_rules=[], **options):
|
||||
view = LazyView('yourapplication.' + import_name)
|
||||
app.add_url_rule(url_rule, view_func=view, **options)
|
||||
for url_rule in url_rules:
|
||||
app.add_url_rule(url_rule, view_func=view, **options)
|
||||
|
||||
url('/', 'views.index')
|
||||
url('/user/<username>', 'views.user')
|
||||
# add a single route to the index view
|
||||
url('views.index', ['/'])
|
||||
|
||||
# add two routes to a single function endpoint
|
||||
url_rules = ['/user/','/user/<username>']
|
||||
url('views.user', url_rules)
|
||||
|
||||
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 properly on the first
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ insert query to the next without any problem. MongoKit is just schemaless
|
|||
too, but implements some validation to ensure data integrity.
|
||||
|
||||
Here is an example document (put this also into :file:`app.py`, e.g.)::
|
||||
|
||||
from mongokit import ValidationError
|
||||
|
||||
def max_length(length):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ if you want to get started quickly.
|
|||
You can download `Flask-SQLAlchemy`_ from `PyPI
|
||||
<https://pypi.python.org/pypi/Flask-SQLAlchemy>`_.
|
||||
|
||||
.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/
|
||||
.. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/
|
||||
|
||||
|
||||
Declarative
|
||||
|
|
@ -33,7 +33,7 @@ SQLAlchemy. It allows you to define tables and models in one go, similar
|
|||
to how Django works. In addition to the following text I recommend the
|
||||
official documentation on the `declarative`_ extension.
|
||||
|
||||
Here the example :file:`database.py` module for your application::
|
||||
Here's the example :file:`database.py` module for your application::
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
|
@ -108,9 +108,9 @@ Querying is simple as well:
|
|||
>>> User.query.filter(User.name == 'admin').first()
|
||||
<User u'admin'>
|
||||
|
||||
.. _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 <http://www.sqlalchemy.org/>`_.
|
||||
`website <https://www.sqlalchemy.org/>`_.
|
||||
|
|
|
|||
|
|
@ -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,8 @@ 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 ``get_db``::
|
||||
dictionaries instead of tuples, this could be inserted into the ``get_db``
|
||||
function we created above::
|
||||
|
||||
def make_dicts(cursor, row):
|
||||
return dict((cursor.description[idx][0], value)
|
||||
|
|
@ -79,21 +80,37 @@ dictionaries instead of tuples, this could be inserted into ``get_db``::
|
|||
|
||||
db.row_factory = make_dicts
|
||||
|
||||
Or even simpler::
|
||||
This will make the sqlite3 module return dicts for this database connection, which are much nicer to deal with. Even more simply, we could place this in ``get_db`` instead::
|
||||
|
||||
db.row_factory = sqlite3.Row
|
||||
|
||||
This would use Row objects rather than dicts to return the results of queries. These are ``namedtuple`` s, so we can access them either by index or by key. For example, assuming we have a ``sqlite3.Row`` called ``r`` for the rows ``id``, ``FirstName``, ``LastName``, and ``MiddleInitial``::
|
||||
|
||||
>>> # You can get values based on the row's name
|
||||
>>> r['FirstName']
|
||||
John
|
||||
>>> # Or, you can get them based on index
|
||||
>>> r[1]
|
||||
John
|
||||
# Row objects are also iterable:
|
||||
>>> for value in r:
|
||||
... print(value)
|
||||
1
|
||||
John
|
||||
Doe
|
||||
M
|
||||
|
||||
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::
|
||||
|
|
@ -114,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
|
||||
<http://en.wikipedia.org/wiki/SQL_injection>`_.
|
||||
<https://en.wikipedia.org/wiki/SQL_injection>`_.
|
||||
|
||||
Initial Schemas
|
||||
---------------
|
||||
|
|
|
|||
17
docs/patterns/subclassing.rst
Normal file
17
docs/patterns/subclassing.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
Subclassing Flask
|
||||
=================
|
||||
|
||||
The :class:`~flask.Flask` class is designed for subclassing.
|
||||
|
||||
For example, you may want to override how request parameters are handled to preserve their order::
|
||||
|
||||
from flask import Flask, Request
|
||||
from werkzeug.datastructures import ImmutableOrderedMultiDict
|
||||
class MyRequest(Request):
|
||||
"""Request subclass to override request parameter storage"""
|
||||
parameter_storage_class = ImmutableOrderedMultiDict
|
||||
class MyFlask(Flask):
|
||||
"""Flask subclass using the custom request class"""
|
||||
request_class = MyRequest
|
||||
|
||||
This is the recommended approach for overriding or augmenting Flask's internal functionality.
|
||||
|
|
@ -16,15 +16,13 @@ Login Required Decorator
|
|||
------------------------
|
||||
|
||||
So let's implement such a decorator. A decorator is a function that
|
||||
returns a function. Pretty simple actually. The only thing you have to
|
||||
keep in mind when implementing something like this is to update the
|
||||
`__name__`, `__module__` and some other attributes of a function. This is
|
||||
often forgotten, but you don't have to do that by hand, there is a
|
||||
function for that that is used like a decorator (:func:`functools.wraps`).
|
||||
wraps and replaces another function. Since the original function is
|
||||
replaced, you need to remember to copy the original function's information
|
||||
to the new function. Use :func:`functools.wraps` to handle this for you.
|
||||
|
||||
This example assumes that the login page is called ``'login'`` and that
|
||||
the current user is stored as `g.user` and ``None`` if there is no-one
|
||||
logged in::
|
||||
the current user is stored in ``g.user`` and is ``None`` if there is no-one
|
||||
logged in. ::
|
||||
|
||||
from functools import wraps
|
||||
from flask import g, request, redirect, url_for
|
||||
|
|
@ -37,15 +35,24 @@ logged in::
|
|||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
So how would you use that decorator now? Apply it as innermost decorator
|
||||
to a view function. When applying further decorators, always remember
|
||||
that the :meth:`~flask.Flask.route` decorator is the outermost::
|
||||
To use the decorator, apply it as innermost decorator to a view function.
|
||||
When applying further decorators, always remember
|
||||
that the :meth:`~flask.Flask.route` decorator is the outermost. ::
|
||||
|
||||
@app.route('/secret_page')
|
||||
@login_required
|
||||
def secret_page():
|
||||
pass
|
||||
|
||||
.. note::
|
||||
The ``next`` value will exist in ``request.args`` after a ``GET`` request for
|
||||
the login page. You'll have to pass it along when sending the ``POST`` request
|
||||
from the login form. You can do this with a hidden input tag, then retrieve it
|
||||
from ``request.form`` when logging the user in. ::
|
||||
|
||||
<input type="hidden" value="{{ request.args.get('next', '') }}"/>
|
||||
|
||||
|
||||
Caching Decorator
|
||||
-----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Form Validation with WTForms
|
||||
============================
|
||||
|
||||
When you have to work with form data submitted by a browser view code
|
||||
When you have to work with form data submitted by a browser view, code
|
||||
quickly becomes very hard to read. There are libraries out there designed
|
||||
to make this process easier to manage. One of them is `WTForms`_ which we
|
||||
will handle here. If you find yourself in the situation of having many
|
||||
|
|
@ -12,14 +12,14 @@ first. I recommend breaking up the application into multiple modules
|
|||
(:ref:`larger-applications`) for that and adding a separate module for the
|
||||
forms.
|
||||
|
||||
.. admonition:: Getting most of WTForms with an Extension
|
||||
.. admonition:: Getting the most out of WTForms with an Extension
|
||||
|
||||
The `Flask-WTF`_ extension expands on this pattern and adds a few
|
||||
handful little helpers that make working with forms and Flask more
|
||||
The `Flask-WTF`_ extension expands on this pattern and adds a
|
||||
few little helpers that make working with forms and Flask more
|
||||
fun. You can get it from `PyPI
|
||||
<https://pypi.python.org/pypi/Flask-WTF>`_.
|
||||
|
||||
.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/
|
||||
.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/
|
||||
|
||||
The Forms
|
||||
---------
|
||||
|
|
@ -32,11 +32,11 @@ This is an example form for a typical registration page::
|
|||
username = StringField('Username', [validators.Length(min=4, max=25)])
|
||||
email = StringField('Email Address', [validators.Length(min=6, max=35)])
|
||||
password = PasswordField('New Password', [
|
||||
validators.Required(),
|
||||
validators.DataRequired(),
|
||||
validators.EqualTo('confirm', message='Passwords must match')
|
||||
])
|
||||
confirm = PasswordField('Repeat Password')
|
||||
accept_tos = BooleanField('I accept the TOS', [validators.Required()])
|
||||
accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])
|
||||
|
||||
In the View
|
||||
-----------
|
||||
|
|
@ -54,8 +54,8 @@ In the view function, the usage of this form looks like this::
|
|||
return redirect(url_for('login'))
|
||||
return render_template('register.html', form=form)
|
||||
|
||||
Notice that we are implying that the view is using SQLAlchemy here
|
||||
(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt
|
||||
Notice we're implying that the view is using SQLAlchemy here
|
||||
(:ref:`sqlalchemy-pattern`), but that's not a requirement, of course. Adapt
|
||||
the code as necessary.
|
||||
|
||||
Things to remember:
|
||||
|
|
@ -64,14 +64,14 @@ Things to remember:
|
|||
the data is submitted via the HTTP ``POST`` method and
|
||||
:attr:`~flask.request.args` if the data is submitted as ``GET``.
|
||||
2. to validate the data, call the :func:`~wtforms.form.Form.validate`
|
||||
method which will return ``True`` if the data validates, ``False``
|
||||
method, which will return ``True`` if the data validates, ``False``
|
||||
otherwise.
|
||||
3. to access individual values from the form, access `form.<NAME>.data`.
|
||||
|
||||
Forms in Templates
|
||||
------------------
|
||||
|
||||
Now to the template side. When you pass the form to the templates you can
|
||||
Now to the template side. When you pass the form to the templates, you can
|
||||
easily render them there. Look at the following example template to see
|
||||
how easy this is. WTForms does half the form generation for us already.
|
||||
To make it even nicer, we can write a macro that renders a field with
|
||||
|
|
@ -95,20 +95,20 @@ Here's an example :file:`_formhelpers.html` template with such a macro:
|
|||
{% endmacro %}
|
||||
|
||||
This macro accepts a couple of keyword arguments that are forwarded to
|
||||
WTForm's field function that renders the field for us. The keyword
|
||||
arguments will be inserted as HTML attributes. So for example you can
|
||||
WTForm's field function, which renders the field for us. The keyword
|
||||
arguments will be inserted as HTML attributes. So, for example, you can
|
||||
call ``render_field(form.username, class='username')`` to add a class to
|
||||
the input element. Note that WTForms returns standard Python unicode
|
||||
strings, so we have to tell Jinja2 that this data is already HTML escaped
|
||||
strings, so we have to tell Jinja2 that this data is already HTML-escaped
|
||||
with the ``|safe`` filter.
|
||||
|
||||
Here the :file:`register.html` template for the function we used above which
|
||||
Here is the :file:`register.html` template for the function we used above, which
|
||||
takes advantage of the :file:`_formhelpers.html` template:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
<form method=post action="/register">
|
||||
<form method=post>
|
||||
<dl>
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.email) }}
|
||||
|
|
@ -122,5 +122,5 @@ takes advantage of the :file:`_formhelpers.html` template:
|
|||
For more information about WTForms, head over to the `WTForms
|
||||
website`_.
|
||||
|
||||
.. _WTForms: http://wtforms.readthedocs.org/
|
||||
.. _WTForms website: http://wtforms.readthedocs.org/
|
||||
.. _WTForms: https://wtforms.readthedocs.io/
|
||||
.. _WTForms website: https://wtforms.readthedocs.io/
|
||||
|
|
|
|||
|
|
@ -3,32 +3,21 @@
|
|||
Python 3 Support
|
||||
================
|
||||
|
||||
Flask and all of its dependencies support Python 3 so you can in theory
|
||||
start working on it already. There are however a few things you should be
|
||||
aware of before you start using Python 3 for your next project.
|
||||
Flask, its dependencies, and most Flask extensions support Python 3.
|
||||
You should start using Python 3 for your next project,
|
||||
but there are a few things to be aware of.
|
||||
|
||||
If you want to use Flask with Python 3 you will need to use Python 3.3 or
|
||||
higher. 3.2 and older are *not* supported.
|
||||
You need to use Python 3.3 or higher. 3.2 and older are *not* supported.
|
||||
|
||||
In addition to that you need to use the latest and greatest versions of
|
||||
`itsdangerous`, `Jinja2` and `Werkzeug`. Flask 0.10 and Werkzeug 0.9 were
|
||||
the first versions to introduce Python 3 support.
|
||||
You should use the latest versions of all Flask-related packages.
|
||||
Flask 0.10 and Werkzeug 0.9 were the first versions to introduce Python 3 support.
|
||||
|
||||
Some of the decisions made in regards to unicode and byte utilization on
|
||||
Python 3 make it hard to write low level code. This mainly affects WSGI
|
||||
middlewares and interacting with the WSGI provided information. Werkzeug
|
||||
wraps all that information in high-level helpers but some of those were
|
||||
specifically added for the Python 3 support and are quite new.
|
||||
Python 3 changed how unicode and bytes are handled, which complicates how low
|
||||
level code handles HTTP data. This mainly affects WSGI middleware interacting
|
||||
with the WSGI ``environ`` data. Werkzeug wraps that information in high-level
|
||||
helpers, so encoding issues should not affect you.
|
||||
|
||||
Unless you require absolute compatibility, you should be fine with Python 3
|
||||
nowadays. Most libraries and Flask extensions have been ported by now and
|
||||
using Flask with Python 3 is generally a smooth ride. However, keep in mind
|
||||
that most libraries (including Werkzeug and Flask) might not quite as stable
|
||||
on Python 3 yet. You might therefore sometimes run into bugs that are
|
||||
usually encoding-related.
|
||||
|
||||
The majority of the upgrade pain is in the lower-level libraries like
|
||||
Flask and Werkzeug and not in the actual high-level application code. For
|
||||
instance all of the Flask examples that are in the Flask repository work
|
||||
out of the box on both 2.x and 3.x and did not require a single line of
|
||||
code changed.
|
||||
The majority of the upgrade work is in the lower-level libraries like
|
||||
Flask and Werkzeug, not the high-level application code.
|
||||
For example, all of the examples in the Flask repository work on both Python 2 and 3
|
||||
and did not require a single line of code changed.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ So what did that code do?
|
|||
class will be our WSGI application.
|
||||
2. Next we create an instance of this class. The first argument is the name of
|
||||
the application's module or package. If you are using a single module (as
|
||||
in this example), you should use `__name__` because depending on if it's
|
||||
in this example), you should use ``__name__`` because depending on if it's
|
||||
started as application or imported as module the name will be different
|
||||
(``'__main__'`` versus the actual import name). This is needed so that
|
||||
Flask knows where to look for templates, static files, and so on. For more
|
||||
|
|
@ -37,19 +37,32 @@ So what did that code do?
|
|||
particular function, and returns the message we want to display in the
|
||||
user's browser.
|
||||
|
||||
Just save it as :file:`hello.py` (or something similar) and run it with your Python
|
||||
interpreter. Make sure to not call your application :file:`flask.py` because this
|
||||
would conflict with Flask itself.
|
||||
Just save it as :file:`hello.py` or something similar. Make sure to not call
|
||||
your application :file:`flask.py` because this would conflict with Flask
|
||||
itself.
|
||||
|
||||
To run the application you can either use the :command:`flask` command or
|
||||
python's :option:`-m` switch with Flask::
|
||||
python's ``-m`` switch with Flask. Before you can do that you need
|
||||
to tell your terminal the application to work with by exporting the
|
||||
``FLASK_APP`` environment variable::
|
||||
|
||||
$ flask -a hello run
|
||||
$ export FLASK_APP=hello.py
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
or alternatively::
|
||||
If you are on Windows, the environment variable syntax depends on command line
|
||||
interpreter. On Command Prompt::
|
||||
|
||||
$ python -m flask -a hello run
|
||||
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`::
|
||||
|
||||
$ export FLASK_APP=hello.py
|
||||
$ python -m flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
This launches a very simple builtin server, which is good enough for testing
|
||||
|
|
@ -72,7 +85,7 @@ should see your hello world greeting.
|
|||
you can make the server publicly available simply by adding
|
||||
``--host=0.0.0.0`` to the command line::
|
||||
|
||||
flask -a hello run --host=0.0.0.0
|
||||
flask run --host=0.0.0.0
|
||||
|
||||
This tells your operating system to listen on all public IPs.
|
||||
|
||||
|
|
@ -80,35 +93,26 @@ should see your hello world greeting.
|
|||
What to do if the Server does not Start
|
||||
---------------------------------------
|
||||
|
||||
In case the ``python -m flask`` fails or :command:`flask` does not exist,
|
||||
In case the :command:`python -m flask` fails or :command:`flask` does not exist,
|
||||
there are multiple reasons this might be the case. First of all you need
|
||||
to look at the error message.
|
||||
|
||||
Old Version of Flask
|
||||
````````````````````
|
||||
|
||||
Versions of Flask older than 1.0 use to have different ways to start the
|
||||
Versions of Flask older than 0.11 use to have different ways to start the
|
||||
application. In short, the :command:`flask` command did not exist, and
|
||||
neither did ``python -m flask``. In that case you have two options:
|
||||
neither did :command:`python -m flask`. In that case you have two options:
|
||||
either upgrade to newer Flask versions or have a look at the :ref:`server`
|
||||
docs to see the alternative method for running a server.
|
||||
|
||||
Python older 2.7
|
||||
````````````````
|
||||
|
||||
In case you have a version of Python older than 2.7 ``python -m flask``
|
||||
does not work. You can either use :command:`flask` or ``python -m
|
||||
flask.cli`` as an alternative. This is because Python before 2.7 does no
|
||||
permit packages to act as executable modules. For more information see
|
||||
:ref:`cli`.
|
||||
|
||||
Invalid Import Name
|
||||
```````````````````
|
||||
|
||||
The :option:`-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.
|
||||
|
|
@ -118,16 +122,21 @@ The most common reason is a typo or because you did not actually create an
|
|||
Debug Mode
|
||||
----------
|
||||
|
||||
(Want to just log errors and stack traces? See :ref:`application-errors`)
|
||||
|
||||
The :command:`flask` script is nice to start a local development server, but
|
||||
you would have to restart it manually after each change to your code.
|
||||
That is not very nice and Flask can do better. If you enable debug
|
||||
support the server will reload itself on code changes, and it will also
|
||||
provide you with a helpful debugger if things go wrong.
|
||||
|
||||
There are different ways to enable the debug mode. The most obvious one
|
||||
is the :option:`--debug` parameter to the :command:`flask` command::
|
||||
To enable debug mode you can export the ``FLASK_DEBUG`` environment variable
|
||||
before running the server::
|
||||
|
||||
flask --debug -a hello run
|
||||
$ export FLASK_DEBUG=1
|
||||
$ flask run
|
||||
|
||||
(On Windows you need to use ``set`` instead of ``export``).
|
||||
|
||||
This does the following things:
|
||||
|
||||
|
|
@ -151,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():
|
||||
|
|
@ -174,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
|
||||
``<variable_name>``. Such a part is then passed as a keyword argument to your
|
||||
function. Optionally a converter can be used by specifying a rule with
|
||||
``<converter:variable_name>``. Here are some nice examples::
|
||||
You can add variable sections to a URL by marking sections with
|
||||
``<variable_name>``. Your function then receives the ``<variable_name>``
|
||||
as a keyword argument. Optionally, you can use a converter to specify the type
|
||||
of the argument like ``<converter:variable_name>``. ::
|
||||
|
||||
@app.route('/user/<username>')
|
||||
def show_user_profile(username):
|
||||
|
|
@ -195,109 +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:
|
||||
@app.route('/path/<path:subpath>')
|
||||
def show_subpath(subpath):
|
||||
# show the subpath after /path/
|
||||
return 'Subpath %s' % subpath
|
||||
|
||||
=========== ===============================================
|
||||
`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
|
||||
=========== ===============================================
|
||||
Converter types:
|
||||
|
||||
.. admonition:: Unique URLs / Redirection Behavior
|
||||
========== ==========================================
|
||||
``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
|
||||
========== ==========================================
|
||||
|
||||
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.
|
||||
Unique URLs / Redirection Behavior
|
||||
``````````````````````````````````
|
||||
|
||||
Take these two rules::
|
||||
Take these two rules::
|
||||
|
||||
@app.route('/projects/')
|
||||
def projects():
|
||||
return 'The project page'
|
||||
@app.route('/projects/')
|
||||
def projects():
|
||||
return 'The project page'
|
||||
|
||||
@app.route('/about')
|
||||
def about():
|
||||
return 'The about page'
|
||||
@app.route('/about')
|
||||
def about():
|
||||
return 'The about 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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/<username>')
|
||||
... 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/<username>')
|
||||
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():
|
||||
|
|
@ -306,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.
|
||||
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.
|
||||
|
||||
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
|
||||
clients 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
|
||||
.. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt
|
||||
|
||||
Static Files
|
||||
------------
|
||||
|
|
@ -442,22 +402,22 @@ 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
|
||||
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 (for example because 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 is a basic introduction to how the :class:`~jinja2.Markup` class works:
|
||||
Here is a basic introduction to how the :class:`~jinja2.Markup` class works::
|
||||
|
||||
>>> from flask import Markup
|
||||
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
|
||||
Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
|
||||
>>> Markup.escape('<blink>hacker</blink>')
|
||||
Markup(u'<blink>hacker</blink>')
|
||||
>>> Markup('<em>Marked up</em> » HTML').striptags()
|
||||
u'Marked up \xbb HTML'
|
||||
>>> from flask import Markup
|
||||
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
|
||||
Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
|
||||
>>> Markup.escape('<blink>hacker</blink>')
|
||||
Markup(u'<blink>hacker</blink>')
|
||||
>>> Markup('<em>Marked up</em> » HTML').striptags()
|
||||
u'Marked up \xbb HTML'
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
|
||||
|
|
@ -534,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::
|
||||
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'])
|
||||
|
|
@ -559,14 +519,14 @@ attributes mentioned above::
|
|||
# was GET or the credentials were invalid
|
||||
return render_template('login.html', error=error)
|
||||
|
||||
What happens if the key does not exist in the `form` attribute? In that
|
||||
What happens if the key does not exist in the ``form`` attribute? In that
|
||||
case a special :exc:`KeyError` is raised. You can catch it like a
|
||||
standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request
|
||||
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', '')
|
||||
|
||||
|
|
@ -575,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
|
||||
|
|
@ -721,17 +681,15 @@ converting return values into response objects is as follows:
|
|||
3. If a tuple is returned the items in the tuple can provide extra
|
||||
information. Such tuples have to be in the form ``(response, status,
|
||||
headers)`` or ``(response, headers)`` where at least one item has
|
||||
to be in the tuple. The `status` value will override the status code
|
||||
and `headers` can be a list or dictionary of additional header values.
|
||||
to be in the tuple. The ``status`` value will override the status code
|
||||
and ``headers`` can be a list or dictionary of additional header values.
|
||||
4. If none of that works, Flask will assume the return value is a
|
||||
valid WSGI application and convert that into a response object.
|
||||
|
||||
If you want to get hold of the resulting response object inside the view
|
||||
you can use the :func:`~flask.make_response` function.
|
||||
|
||||
Imagine you have a view like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
Imagine you have a view like this::
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
|
|
@ -739,9 +697,7 @@ Imagine you have a view like this:
|
|||
|
||||
You just need to wrap the return expression with
|
||||
:func:`~flask.make_response` and get the response object to modify it, then
|
||||
return it:
|
||||
|
||||
.. sourcecode:: python
|
||||
return it::
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
|
|
@ -780,7 +736,7 @@ sessions work::
|
|||
session['username'] = request.form['username']
|
||||
return redirect(url_for('index'))
|
||||
return '''
|
||||
<form action="" method="post">
|
||||
<form method="post">
|
||||
<p><input type=text name=username>
|
||||
<p><input type=submit value=Login>
|
||||
</form>
|
||||
|
|
@ -803,13 +759,13 @@ not using the template engine (as in this example).
|
|||
The problem with random is that it's hard to judge what is truly random. And
|
||||
a secret key should be as random as possible. Your operating system
|
||||
has ways to generate pretty random stuff based on a cryptographic
|
||||
random generator which can be used to get such a key:
|
||||
random generator which can be used to get such a key::
|
||||
|
||||
>>> import os
|
||||
>>> os.urandom(24)
|
||||
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
|
||||
>>> import os
|
||||
>>> os.urandom(24)
|
||||
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
|
||||
|
||||
Just take that thing and copy/paste it into your code and you're done.
|
||||
Just take that thing and copy/paste it into your code and you're done.
|
||||
|
||||
A note on cookie-based sessions: Flask will take the values you put into the
|
||||
session object and serialize them into a cookie. If you are finding some
|
||||
|
|
@ -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
|
||||
----------------
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ find a piece of code that looks very much like this::
|
|||
with self.request_context(environ):
|
||||
try:
|
||||
response = self.full_dispatch_request()
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
response = self.make_response(self.handle_exception(e))
|
||||
return response(environ, start_response)
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
<http://en.wikipedia.org/wiki/Cross-site_scripting>`_.
|
||||
<https://en.wikipedia.org/wiki/Cross-site_scripting>`_.
|
||||
|
||||
Flask configures Jinja2 to automatically escape all values unless
|
||||
explicitly told otherwise. This should rule out all XSS problems caused
|
||||
|
|
@ -73,7 +73,7 @@ them knowing.
|
|||
Say you have a specific URL that, when you sent ``POST`` requests to will
|
||||
delete a user's profile (say ``http://example.com/user/delete``). If an
|
||||
attacker now creates a page that sends a post request to that page with
|
||||
some JavaScript they just has to trick some users to load that page and
|
||||
some JavaScript they just have to trick some users to load that page and
|
||||
their profiles will end up being deleted.
|
||||
|
||||
Imagine you were to run Facebook with millions of concurrent users and
|
||||
|
|
@ -95,81 +95,12 @@ the form validation framework, which does not exist in Flask.
|
|||
JSON Security
|
||||
-------------
|
||||
|
||||
.. admonition:: ECMAScript 5 Changes
|
||||
In Flask 0.10 and lower, :func:`~flask.jsonify` did not serialize top-level
|
||||
arrays to JSON. This was because of a security vulnerability in ECMAScript 4.
|
||||
|
||||
Starting with ECMAScript 5 the behavior of literals changed. Now they
|
||||
are not constructed with the constructor of ``Array`` and others, but
|
||||
with the builtin constructor of ``Array`` which closes this particular
|
||||
attack vector.
|
||||
|
||||
JSON itself is a high-level serialization format, so there is barely
|
||||
anything that could cause security problems, right? You can't declare
|
||||
recursive structures that could cause problems and the only thing that
|
||||
could possibly break are very large responses that can cause some kind of
|
||||
denial of service at the receiver's side.
|
||||
|
||||
However there is a catch. Due to how browsers work the CSRF issue comes
|
||||
up with JSON unfortunately. Fortunately there is also a weird part of the
|
||||
JavaScript specification that can be used to solve that problem easily and
|
||||
Flask is kinda doing that for you by preventing you from doing dangerous
|
||||
stuff. Unfortunately that protection is only there for
|
||||
:func:`~flask.jsonify` so you are still at risk when using other ways to
|
||||
generate JSON.
|
||||
|
||||
So what is the issue and how to avoid it? The problem are arrays at
|
||||
top-level in JSON. Imagine you send the following data out in a JSON
|
||||
request. Say that's exporting the names and email addresses of all your
|
||||
friends for a part of the user interface that is written in JavaScript.
|
||||
Not very uncommon:
|
||||
|
||||
.. sourcecode:: javascript
|
||||
|
||||
[
|
||||
{"username": "admin",
|
||||
"email": "admin@localhost"}
|
||||
]
|
||||
|
||||
And it is doing that of course only as long as you are logged in and only
|
||||
for you. And it is doing that for all ``GET`` requests to a certain URL,
|
||||
say the URL for that request is
|
||||
``http://example.com/api/get_friends.json``.
|
||||
|
||||
So now what happens if a clever hacker is embedding this to his website
|
||||
and social engineers a victim to visiting his site:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
<script type=text/javascript>
|
||||
var captured = [];
|
||||
var oldArray = Array;
|
||||
function Array() {
|
||||
var obj = this, id = 0, capture = function(value) {
|
||||
obj.__defineSetter__(id++, capture);
|
||||
if (value)
|
||||
captured.push(value);
|
||||
};
|
||||
capture();
|
||||
}
|
||||
</script>
|
||||
<script type=text/javascript
|
||||
src=http://example.com/api/get_friends.json></script>
|
||||
<script type=text/javascript>
|
||||
Array = oldArray;
|
||||
// now we have all the data in the captured array.
|
||||
</script>
|
||||
|
||||
If you know a bit of JavaScript internals you might know that it's
|
||||
possible to patch constructors and register callbacks for setters. An
|
||||
attacker can use this (like above) to get all the data you exported in
|
||||
your JSON file. The browser will totally ignore the :mimetype:`application/json`
|
||||
mimetype if :mimetype:`text/javascript` is defined as content type in the script
|
||||
tag and evaluate that as JavaScript. Because top-level array elements are
|
||||
allowed (albeit useless) and we hooked in our own constructor, after that
|
||||
page loaded the data from the JSON response is in the `captured` array.
|
||||
|
||||
Because it is a syntax error in JavaScript to have an object literal
|
||||
(``{...}``) toplevel an attacker could not just do a request to an
|
||||
external URL with the script tag to load up the data. So what Flask does
|
||||
is to only allow objects as toplevel elements when using
|
||||
:func:`~flask.jsonify`. Make sure to do the same when using an ordinary
|
||||
JSON generate function.
|
||||
ECMAScript 5 closed this vulnerability, so only extremely old browsers are
|
||||
still vulnerable. All of these browsers have `other more serious
|
||||
vulnerabilities
|
||||
<https://github.com/pallets/flask/issues/248#issuecomment-59934857>`_, so
|
||||
this behavior was changed and :func:`~flask.jsonify` now supports serializing
|
||||
arrays.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Development Server
|
|||
|
||||
.. currentmodule:: flask
|
||||
|
||||
Starting with Flask 1.0 there are multiple built-in ways to run a
|
||||
Starting with Flask 0.11 there are multiple built-in ways to run a
|
||||
development server. The best one is the :command:`flask` command line utility
|
||||
but you can also continue using the :meth:`Flask.run` method.
|
||||
|
||||
|
|
@ -16,7 +16,9 @@ The :command:`flask` command line script (:ref:`cli`) is strongly recommended fo
|
|||
development because it provides a superior reload experience due to how it
|
||||
loads the application. The basic usage is like this::
|
||||
|
||||
$ flask -a my_application --debug run
|
||||
$ export FLASK_APP=my_application
|
||||
$ export FLASK_DEBUG=1
|
||||
$ flask run
|
||||
|
||||
This will enable the debugger, the reloader and then start the server on
|
||||
*http://localhost:5000/*.
|
||||
|
|
@ -25,7 +27,7 @@ The individual features of the server can be controlled by passing more
|
|||
arguments to the ``run`` option. For instance the reloader can be
|
||||
disabled::
|
||||
|
||||
$ flask -a my_application --debug run --no-reload
|
||||
$ flask run --no-reload
|
||||
|
||||
In Code
|
||||
-------
|
||||
|
|
@ -40,11 +42,11 @@ Example::
|
|||
app.run()
|
||||
|
||||
This works well for the common case but it does not work well for
|
||||
development which is why from Flask 1.0 onwards the :command:`flask` method is
|
||||
recommended. The reason for this is that due to how the reload mechanism
|
||||
works there are some bizarre side-effects (like executing certain code
|
||||
twice, sometimes crashing without message or dying when a syntax or
|
||||
import error happens).
|
||||
development which is why from Flask 0.11 onwards the :command:`flask`
|
||||
method is recommended. The reason for this is that due to how the reload
|
||||
mechanism works there are some bizarre side-effects (like executing
|
||||
certain code twice, sometimes crashing without message or dying when a
|
||||
syntax or import error happens).
|
||||
|
||||
It is however still a perfectly valid method for invoking a non automatic
|
||||
reloading application.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ chapter of the documentation first.
|
|||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
Starting with Flask 1.0 the recommended way to work with the shell is the
|
||||
Starting with Flask 0.11 the recommended way to work with the shell is the
|
||||
``flask shell`` command which does a lot of this automatically for you.
|
||||
For instance the shell is automatically initialized with a loaded
|
||||
application context.
|
||||
|
|
|
|||
174
docs/signals.rst
174
docs/signals.rst
|
|
@ -19,15 +19,15 @@ more. Also keep in mind that signals are intended to notify subscribers
|
|||
and should not encourage subscribers to modify data. You will notice that
|
||||
there are signals that appear to do the same thing like some of the
|
||||
builtin decorators do (eg: :data:`~flask.request_started` is very similar
|
||||
to :meth:`~flask.Flask.before_request`). There are however difference in
|
||||
how they work. The core :meth:`~flask.Flask.before_request` handler for
|
||||
example is executed in a specific order and is able to abort the request
|
||||
to :meth:`~flask.Flask.before_request`). However, there are differences in
|
||||
how they work. The core :meth:`~flask.Flask.before_request` handler, for
|
||||
example, is executed in a specific order and is able to abort the request
|
||||
early by returning a response. In contrast all signal handlers are
|
||||
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::
|
||||
|
||||
|
|
@ -184,169 +184,7 @@ With Blinker 1.1 you can also easily subscribe to signals by using the new
|
|||
Core Signals
|
||||
------------
|
||||
|
||||
.. when modifying this list, also update the one in api.rst
|
||||
Take a look at :ref:`core-signals-list` for a list of all builtin signals.
|
||||
|
||||
The following signals exist in Flask:
|
||||
|
||||
.. data:: flask.template_rendered
|
||||
:noindex:
|
||||
|
||||
This signal is sent when a template was successfully rendered. The
|
||||
signal is invoked with the instance of the template as `template`
|
||||
and the context as dictionary (named `context`).
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_template_renders(sender, template, context, **extra):
|
||||
sender.logger.debug('Rendering template "%s" with context %s',
|
||||
template.name or 'string template',
|
||||
context)
|
||||
|
||||
from flask import template_rendered
|
||||
template_rendered.connect(log_template_renders, app)
|
||||
|
||||
.. data:: flask.request_started
|
||||
:noindex:
|
||||
|
||||
This signal is sent when the request context is set up, before
|
||||
any request processing happens. Because the request context is already
|
||||
bound, the subscriber can access the request with the standard global
|
||||
proxies such as :class:`~flask.request`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_request(sender, **extra):
|
||||
sender.logger.debug('Request context is set up')
|
||||
|
||||
from flask import request_started
|
||||
request_started.connect(log_request, app)
|
||||
|
||||
.. data:: flask.request_finished
|
||||
:noindex:
|
||||
|
||||
This signal is sent right before the response is sent to the client.
|
||||
It is passed the response to be sent named `response`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_response(sender, response, **extra):
|
||||
sender.logger.debug('Request context is about to close down. '
|
||||
'Response: %s', response)
|
||||
|
||||
from flask import request_finished
|
||||
request_finished.connect(log_response, app)
|
||||
|
||||
.. data:: flask.got_request_exception
|
||||
:noindex:
|
||||
|
||||
This signal is sent when an exception happens during request processing.
|
||||
It is sent *before* the standard exception handling kicks in and even
|
||||
in debug mode, where no exception handling happens. The exception
|
||||
itself is passed to the subscriber as `exception`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_exception(sender, exception, **extra):
|
||||
sender.logger.debug('Got exception during processing: %s', exception)
|
||||
|
||||
from flask import got_request_exception
|
||||
got_request_exception.connect(log_exception, app)
|
||||
|
||||
.. data:: flask.request_tearing_down
|
||||
:noindex:
|
||||
|
||||
This signal is sent when the request is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def close_db_connection(sender, **extra):
|
||||
session.close()
|
||||
|
||||
from flask import request_tearing_down
|
||||
request_tearing_down.connect(close_db_connection, app)
|
||||
|
||||
As of Flask 0.9, this will also be passed an `exc` keyword argument
|
||||
that has a reference to the exception that caused the teardown if
|
||||
there was one.
|
||||
|
||||
.. data:: flask.appcontext_tearing_down
|
||||
:noindex:
|
||||
|
||||
This signal is sent when the app context is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def close_db_connection(sender, **extra):
|
||||
session.close()
|
||||
|
||||
from flask import appcontext_tearing_down
|
||||
appcontext_tearing_down.connect(close_db_connection, app)
|
||||
|
||||
This will also be passed an `exc` keyword argument that has a reference
|
||||
to the exception that caused the teardown if there was one.
|
||||
|
||||
.. data:: flask.appcontext_pushed
|
||||
:noindex:
|
||||
|
||||
This signal is sent when an application context is pushed. The sender
|
||||
is the application. This is usually useful for unittests in order to
|
||||
temporarily hook in information. For instance it can be used to
|
||||
set a resource early onto the `g` object.
|
||||
|
||||
Example usage::
|
||||
|
||||
from contextlib import contextmanager
|
||||
from flask import appcontext_pushed
|
||||
|
||||
@contextmanager
|
||||
def user_set(app, user):
|
||||
def handler(sender, **kwargs):
|
||||
g.user = user
|
||||
with appcontext_pushed.connected_to(handler, app):
|
||||
yield
|
||||
|
||||
And in the testcode::
|
||||
|
||||
def test_user_me(self):
|
||||
with user_set(app, 'john'):
|
||||
c = app.test_client()
|
||||
resp = c.get('/users/me')
|
||||
assert resp.data == 'username=john'
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. data:: flask.appcontext_popped
|
||||
:noindex:
|
||||
|
||||
This signal is sent when an application context is popped. The sender
|
||||
is the application. This usually falls in line with the
|
||||
:data:`appcontext_tearing_down` signal.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
|
||||
.. data:: flask.message_flashed
|
||||
:noindex:
|
||||
|
||||
This signal is sent when the application is flashing a message. The
|
||||
messages is sent as `message` keyword argument and the category as
|
||||
`category`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
recorded = []
|
||||
def record(sender, message, category, **extra):
|
||||
recorded.append((message, category))
|
||||
|
||||
from flask import message_flashed
|
||||
message_flashed.connect(record, app)
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. _blinker: https://pypi.python.org/pypi/blinker
|
||||
|
|
|
|||
|
|
@ -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::
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ Jinja Setup
|
|||
Unless customized, Jinja2 is configured by Flask as follows:
|
||||
|
||||
- autoescaping is enabled for all templates ending in ``.html``,
|
||||
``.htm``, ``.xml`` as well as ``.xhtml``
|
||||
``.htm``, ``.xml`` as well as ``.xhtml`` when using
|
||||
:func:`~flask.templating.render_template`.
|
||||
- autoescaping is enabled for all strings when using
|
||||
:func:`~flask.templating.render_template_string`.
|
||||
- a template has the ability to opt in/out autoescaping with the
|
||||
``{% autoescape %}`` tag.
|
||||
- Flask inserts a couple of global functions and helpers into the
|
||||
|
|
@ -129,7 +132,7 @@ characters in text, but can also lead to security problems. (see
|
|||
|
||||
Sometimes however you will need to disable autoescaping in templates.
|
||||
This can be the case if you want to explicitly inject HTML into pages, for
|
||||
example if they come from a system that generate secure HTML like a
|
||||
example if they come from a system that generates secure HTML like a
|
||||
markdown to HTML converter.
|
||||
|
||||
There are three ways to accomplish that:
|
||||
|
|
|
|||
218
docs/testing.rst
218
docs/testing.rst
|
|
@ -5,114 +5,122 @@ 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/mitsuhiko/flask/tree/master/examples/flaskr/
|
||||
https://github.com/pallets/flask/tree/master/examples/flaskr/
|
||||
|
||||
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
|
||||
|
||||
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()
|
||||
from flaskr import flaskr
|
||||
|
||||
def tearDown(self):
|
||||
os.close(self.db_fd)
|
||||
os.unlink(flaskr.app.config['DATABASE'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@pytest.fixture
|
||||
def client():
|
||||
db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
|
||||
flaskr.app.config['TESTING'] = True
|
||||
client = flaskr.app.test_client()
|
||||
|
||||
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.
|
||||
with flaskr.app.app_context():
|
||||
flaskr.init_db()
|
||||
|
||||
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.
|
||||
yield client
|
||||
|
||||
Because SQLite3 is filesystem-based we can easily use the tempfile module
|
||||
os.close(db_fd)
|
||||
os.unlink(flaskr.app.config['DATABASE'])
|
||||
|
||||
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.
|
||||
|
||||
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 :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
|
||||
|
||||
OK
|
||||
================ test session starts ================
|
||||
rootdir: ./flask/examples/flaskr, inifile: setup.cfg
|
||||
collected 1 items
|
||||
|
||||
tests/test_flaskr.py::test_empty_db PASSED
|
||||
|
||||
============= 1 passed in 0.10 seconds ==============
|
||||
|
||||
Logging In and Out
|
||||
------------------
|
||||
|
|
@ -136,66 +147,77 @@ 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(self, username, password):
|
||||
return self.app.post('/login', data=dict(
|
||||
username=username,
|
||||
password=password
|
||||
), follow_redirects=True)
|
||||
def login(client, username, password):
|
||||
return client.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::
|
||||
invalid credentials. Add this new test function::
|
||||
|
||||
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
|
||||
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='<Hello>',
|
||||
text='<strong>HTML</strong> allowed here'
|
||||
), follow_redirects=True)
|
||||
assert 'No entries here so far' not in rv.data
|
||||
assert '<Hello>' in rv.data
|
||||
assert '<strong>HTML</strong> allowed here' in rv.data
|
||||
assert b'No entries here so far' not in rv.data
|
||||
assert b'<Hello>' in rv.data
|
||||
assert b'<strong>HTML</strong> 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
|
||||
|
||||
OK
|
||||
================ 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
|
||||
|
||||
============= 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/mitsuhiko/flask/tree/master/examples/minitwit/
|
||||
|
||||
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'):
|
||||
|
|
@ -223,8 +245,8 @@ there does not seem to be a good way to do that, consider switching to
|
|||
application factories (see :ref:`app-factories`).
|
||||
|
||||
Note however that if you are using a test request context, the
|
||||
:meth:`~flask.Flask.before_request` functions are not automatically called
|
||||
same for :meth:`~flask.Flask.after_request` functions. However
|
||||
:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request`
|
||||
functions are not called automatically. However
|
||||
:meth:`~flask.Flask.teardown_request` functions are indeed executed when
|
||||
the test request context leaves the ``with`` block. If you do want the
|
||||
:meth:`~flask.Flask.before_request` functions to be called as well, you
|
||||
|
|
@ -274,7 +296,7 @@ this code to get the current user::
|
|||
return user
|
||||
|
||||
For a test it would be nice to override this user from the outside without
|
||||
having to change some code. This can trivially be accomplished with
|
||||
having to change some code. This can be accomplished with
|
||||
hooking the :data:`flask.appcontext_pushed` signal::
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
.. _tutorial-css:
|
||||
|
||||
Step 7: Adding Style
|
||||
Step 8: Adding Style
|
||||
====================
|
||||
|
||||
Now that everything else works, it's time to add some style to the
|
||||
application. Just create a stylesheet called :file:`style.css` in the
|
||||
:file:`static` folder we created before:
|
||||
:file:`static` folder:
|
||||
|
||||
.. sourcecode:: css
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
.. _tutorial-dbcon:
|
||||
|
||||
Step 3: Database Connections
|
||||
Step 4: Database Connections
|
||||
----------------------------
|
||||
|
||||
We have created a function for establishing a database connection with
|
||||
`connect_db`, but by itself, that's not particularly useful. Creating and
|
||||
closing database connections all the time is very inefficient, so we want
|
||||
to keep it around for longer. Because database connections encapsulate a
|
||||
transaction, we also need to make sure that only one request at the time
|
||||
uses the connection. How can we elegantly do that with Flask?
|
||||
Let's continue building our code in the ``flaskr.py`` file.
|
||||
(Scroll to the end of the page for more about project layout.)
|
||||
|
||||
This is where the application context comes into play, so let's start
|
||||
there.
|
||||
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
|
||||
encapsulate a transaction, you will need to make sure that only one
|
||||
request at a time uses the connection. An elegant way to do this is by
|
||||
utilizing the *application context*.
|
||||
|
||||
Flask provides us with two contexts: the application context and the
|
||||
request context. For the time being, all you have to know is that there
|
||||
Flask provides two contexts: the *application context* and the
|
||||
*request context*. For the time being, all you have to know is that there
|
||||
are special variables that use these. For instance, the
|
||||
:data:`~flask.request` variable is the request object associated with
|
||||
the current request, whereas :data:`~flask.g` is a general purpose
|
||||
variable associated with the current application context. We will go into
|
||||
the details of this a bit later.
|
||||
variable associated with the current application context. The tutorial
|
||||
will cover some more details of this later on.
|
||||
|
||||
For the time being, all you have to know is that you can store information
|
||||
safely on the :data:`~flask.g` object.
|
||||
|
|
@ -37,8 +38,7 @@ already established connection::
|
|||
g.sqlite_db = connect_db()
|
||||
return g.sqlite_db
|
||||
|
||||
|
||||
So now we know how to connect, but how do we properly disconnect? For
|
||||
Now you know how to connect, but how can you properly disconnect? For
|
||||
that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext`
|
||||
decorator. It's executed every time the application context tears down::
|
||||
|
||||
|
|
@ -75,4 +75,4 @@ Continue to :ref:`tutorial-dbinit`.
|
|||
larger <larger-applications>`, it's a good idea not to.
|
||||
|
||||
.. _example source:
|
||||
https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
|
||||
https://github.com/pallets/flask/tree/master/examples/flaskr/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.. _tutorial-dbinit:
|
||||
|
||||
Step 4: Creating The Database
|
||||
Step 5: Creating The Database
|
||||
=============================
|
||||
|
||||
As outlined earlier, Flaskr is a database powered application, and more
|
||||
|
|
@ -9,58 +9,66 @@ 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 we 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, we can create a function and hook it into the :command:`flask`
|
||||
command that initializes the database. Let me show you the code first. Just
|
||||
add this function 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
|
||||
create a application context for us bound to the right application.
|
||||
Within the function, we can then access :attr:`flask.g` and other things as
|
||||
we would expect. When the script ends, the application context tears down
|
||||
create an application context which is bound to the right application.
|
||||
Within the function, you can then access :attr:`flask.g` and other things as
|
||||
you might expect. When the script ends, the application context tears down
|
||||
and the database connection is released.
|
||||
|
||||
We want to keep an actual functions around that initializes the database,
|
||||
You will want to keep an actual function around that initializes the database,
|
||||
though, so that we can easily create databases in unit tests later on. (For
|
||||
more information see :ref:`testing`.)
|
||||
|
||||
The :func:`~flask.Flask.open_resource` method of the application object
|
||||
is a convenient helper function that will open a resource that the
|
||||
application provides. This function opens a file from the resource
|
||||
location (your ``flaskr`` folder) and allows you to read from it. We are
|
||||
using this here to execute a script on the database connection.
|
||||
location (the :file:`flaskr/flaskr` folder) and allows you to read from it.
|
||||
It is used in this example to execute a script on the database connection.
|
||||
|
||||
The connection object provided by SQLite can give us a cursor object.
|
||||
On that cursor, there is a method to execute a complete script. Finally, we
|
||||
The connection object provided by SQLite can give you a cursor object.
|
||||
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 --app=flaskr initdb
|
||||
flask initdb
|
||||
Initialized the database.
|
||||
|
||||
.. admonition:: Troubleshooting
|
||||
|
|
|
|||
|
|
@ -3,21 +3,29 @@
|
|||
Step 0: Creating The Folders
|
||||
============================
|
||||
|
||||
Before we get started, let's 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
|
||||
/static
|
||||
/templates
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
|
||||
The ``flaskr`` folder is not a Python package, but just something where we
|
||||
drop our files. Later on, we will put our database schema as well as main
|
||||
module into this folder. It is done in the following way. The files inside
|
||||
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 `Jinja2`_ templates. The
|
||||
templates you create later on in the tutorial will go in this directory.
|
||||
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.
|
||||
|
||||
Continue with :ref:`tutorial-schema`.
|
||||
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
|
||||
`Jinja2`_ templates. You will see examples of this later on.
|
||||
|
||||
For now you should continue with :ref:`tutorial-schema`.
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/
|
||||
|
|
|
|||
|
|
@ -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/mitsuhiko/flask/tree/master/examples/flaskr/
|
||||
.. _example source: https://github.com/pallets/flask/tree/master/examples/flaskr/
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -24,6 +24,7 @@ the `example source`_.
|
|||
folders
|
||||
schema
|
||||
setup
|
||||
packaging
|
||||
dbcon
|
||||
dbinit
|
||||
views
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
Introducing Flaskr
|
||||
==================
|
||||
|
||||
We will call our blogging application flaskr, but feel free to choose your own
|
||||
less Web-2.0-ish name ;) Essentially, we want it to do the following things:
|
||||
This tutorial will demonstrate a blogging application named Flaskr, but feel
|
||||
free to choose your own less Web-2.0-ish name ;) Essentially, it will do the
|
||||
following things:
|
||||
|
||||
1. Let the user sign in and out with credentials specified in the
|
||||
configuration. Only one user is supported.
|
||||
|
|
@ -14,14 +15,14 @@ less Web-2.0-ish name ;) Essentially, we want it to do the following things:
|
|||
3. The index page shows all entries so far in reverse chronological order
|
||||
(newest on top) and the user can add new ones from there if logged in.
|
||||
|
||||
We will be using SQLite3 directly for this application because it's good
|
||||
enough for an application of this size. For larger applications, however,
|
||||
SQLite3 will be used directly for this application because it's good enough
|
||||
for an application of this size. For larger applications, however,
|
||||
it makes a lot of sense to use `SQLAlchemy`_, as it handles database
|
||||
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
|
||||
|
|
@ -30,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/
|
||||
|
|
|
|||
106
docs/tutorial/packaging.rst
Normal file
106
docs/tutorial/packaging.rst
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
.. _tutorial-packaging:
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
Create the ``setup.py`` file for ``flaskr`` with the following content::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='flaskr',
|
||||
packages=['flaskr'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
)
|
||||
|
||||
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::
|
||||
|
||||
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, from the ``flaskr/`` directory, go ahead and install the
|
||||
application with::
|
||||
|
||||
pip install --editable .
|
||||
|
||||
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 on Mac or Linux with the following commands in ``flaskr/``::
|
||||
|
||||
export FLASK_APP=flaskr
|
||||
export FLASK_DEBUG=true
|
||||
flask run
|
||||
|
||||
(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 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,
|
||||
but first, you should get the database working.
|
||||
|
||||
.. admonition:: Externally Visible Server
|
||||
|
||||
Want your server to be publicly available? Check out the
|
||||
:ref:`externally visible server <public-server>` section for more
|
||||
information.
|
||||
|
||||
Continue with :ref:`tutorial-dbcon`.
|
||||
|
||||
.. _Click: http://click.pocoo.org
|
||||
.. _Python Packaging Guide: https://packaging.python.org
|
||||
.. _virtualenv: https://virtualenv.pypa.io
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
Step 1: Database Schema
|
||||
=======================
|
||||
|
||||
First, we want to create the database schema. Only a single table is needed
|
||||
for this application and we only want to support SQLite, so creating the
|
||||
database schema is quite easy. Just put the following contents into a file
|
||||
named `schema.sql` in the just created `flaskr` folder:
|
||||
In this step, you will create the database schema. Only a single table is
|
||||
needed for this application and it will only support SQLite. All you need to do
|
||||
is put the following contents into a file named :file:`schema.sql` in the
|
||||
:file:`flaskr/flaskr` folder:
|
||||
|
||||
.. sourcecode:: sql
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ named `schema.sql` in the just created `flaskr` folder:
|
|||
'text' text not null
|
||||
);
|
||||
|
||||
This schema consists of a single table called ``entries``. Each row in
|
||||
This schema consists of a single table called ``entries``. 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.
|
||||
|
|
|
|||
|
|
@ -3,28 +3,34 @@
|
|||
Step 2: Application Setup Code
|
||||
==============================
|
||||
|
||||
Now that we have the schema in place, we can create the application module.
|
||||
Let's call it ``flaskr.py``. We will place this file inside the ``flaskr``
|
||||
folder. We will begin by adding the imports we need and by adding the config
|
||||
section. For small applications, it is possible to drop the configuration
|
||||
directly into the module, and this is what we will be doing here. However,
|
||||
a cleaner solution would be 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.
|
||||
|
||||
First, we add the imports in :file:`flaskr.py`::
|
||||
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
|
||||
|
||||
Next, we can create our actual application 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)
|
||||
|
||||
# create our little application :)
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(__name__)
|
||||
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
|
||||
|
||||
# Load default config and override config from an environment variable
|
||||
app.config.update(dict(
|
||||
|
|
@ -35,8 +41,8 @@ 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 we
|
||||
can update it 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
|
||||
|
||||
|
|
@ -55,16 +61,16 @@ can update it 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.
|
||||
|
||||
.. code-block:: python
|
||||
will use the setting defined in the last import. This enables robust
|
||||
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
|
||||
|
|
@ -74,42 +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.
|
||||
|
||||
We will also 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. We create a simple database connection through SQLite and
|
||||
then tell it to use the :class:`sqlite3.Row` object to represent rows.
|
||||
This allows us to treat the rows as if they were dictionaries instead of
|
||||
tuples.
|
||||
|
||||
::
|
||||
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
|
||||
|
||||
With that out of the way, you should be able to start up the application
|
||||
without problems. Do this with the following command::
|
||||
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.
|
||||
|
||||
flask --app=flaskr --debug run
|
||||
In the next section you will see how to run the application.
|
||||
|
||||
The :option:`--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.
|
||||
|
||||
When you head over to the server in your browser, you will get a 404 error
|
||||
because we don't have any views yet. We will focus on that a little later,
|
||||
but first, we should get the database working.
|
||||
|
||||
.. admonition:: Externally Visible Server
|
||||
|
||||
Want your server to be publicly available? Check out the
|
||||
:ref:`externally visible server <public-server>` section for more
|
||||
information.
|
||||
|
||||
Continue with :ref:`tutorial-dbcon`.
|
||||
Continue with :ref:`tutorial-packaging`.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
.. _tutorial-templates:
|
||||
|
||||
Step 6: The Templates
|
||||
Step 7: The Templates
|
||||
=====================
|
||||
|
||||
Now we should start working on the templates. If we were to request the URLs
|
||||
now, we would only get an exception that Flask cannot find the templates. The
|
||||
templates are using `Jinja2`_ syntax and have autoescaping enabled by
|
||||
Now it is time to start working on the templates. As you may have
|
||||
noticed, if you make requests with the app running, you will get
|
||||
an exception that Flask cannot find the templates. The templates
|
||||
are using `Jinja2`_ syntax and have autoescaping enabled by
|
||||
default. This means that unless you mark a value in the code with
|
||||
:class:`~flask.Markup` or with the ``|safe`` filter in the template,
|
||||
Jinja2 will ensure that special characters such as ``<`` or ``>`` are
|
||||
|
|
@ -14,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
|
||||
|
||||
|
|
@ -57,9 +59,9 @@ 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. We also tell the
|
||||
form to submit to your `add_entry` function and use ``POST`` as HTTP
|
||||
method:
|
||||
in with the :func:`~flask.render_template` function. Notice that the form is
|
||||
configured to submit to the `add_entry` view function and use ``POST`` as
|
||||
HTTP method:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -78,9 +80,9 @@ method:
|
|||
{% endif %}
|
||||
<ul class=entries>
|
||||
{% for entry in entries %}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}</li>
|
||||
{% else %}
|
||||
<li><em>Unbelievable. No entries here so far</em>
|
||||
<li><em>Unbelievable. No entries here so far</em></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -8,3 +8,89 @@ expected, it's probably not a bad idea to add automated tests to simplify
|
|||
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
|
||||
----------------------
|
||||
|
||||
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.
|
||||
|
||||
One possible and recommended project structure is::
|
||||
|
||||
flaskr/
|
||||
flaskr/
|
||||
__init__.py
|
||||
static/
|
||||
templates/
|
||||
tests/
|
||||
test_flaskr.py
|
||||
setup.py
|
||||
MANIFEST.in
|
||||
|
||||
For now go ahead a create the :file:`tests/` directory as well as the
|
||||
:file:`test_flaskr.py` file.
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
|
||||
At this point you can run the tests. Here ``pytest`` will be used.
|
||||
|
||||
.. 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::
|
||||
|
||||
pip install -e .
|
||||
pip install pytest
|
||||
|
||||
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
|
||||
|
||||
setup(
|
||||
name='flaskr',
|
||||
packages=['flaskr'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
setup_requires=[
|
||||
'pytest-runner',
|
||||
],
|
||||
tests_require=[
|
||||
'pytest',
|
||||
],
|
||||
)
|
||||
|
||||
Now create :file:`setup.cfg` in the project root (alongside
|
||||
:file:`setup.py`)::
|
||||
|
||||
[aliases]
|
||||
test=pytest
|
||||
|
||||
Now you can run::
|
||||
|
||||
python setup.py test
|
||||
|
||||
This calls on the alias created in :file:`setup.cfg` which in turn runs
|
||||
``pytest`` via ``pytest-runner``, as the :file:`setup.py` script has
|
||||
been called. (Recall the `setup_requires` argument in :file:`setup.py`)
|
||||
Following the standard rules of test-discovery your tests will be
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
.. _tutorial-views:
|
||||
|
||||
Step 5: The View Functions
|
||||
Step 6: The View Functions
|
||||
==========================
|
||||
|
||||
Now that the database connections are working, we can start writing the
|
||||
view functions. We will need four of them:
|
||||
Now that the database connections are working, you can start writing the
|
||||
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
|
||||
------------
|
||||
|
|
@ -12,11 +13,11 @@ Show Entries
|
|||
This view shows all the entries stored in the database. It listens on the
|
||||
root of the application and will select title and text from the database.
|
||||
The one with the highest id (the newest entry) will be on top. The rows
|
||||
returned from the cursor look a bit like tuples because we are using
|
||||
returned from the cursor look a bit like dictionaries because we are using
|
||||
the :class:`sqlite3.Row` row factory.
|
||||
|
||||
The view function will pass the entries as dictionaries to the
|
||||
:file:`show_entries.html` template and return the rendered one::
|
||||
The view function will pass the entries to the :file:`show_entries.html`
|
||||
template and return the rendered one::
|
||||
|
||||
@app.route('/')
|
||||
def show_entries():
|
||||
|
|
@ -30,7 +31,7 @@ Add New Entry
|
|||
|
||||
This view lets the user add new entries if they are logged in. This only
|
||||
responds to ``POST`` requests; the actual form is shown on the
|
||||
`show_entries` page. If everything worked out well, we will
|
||||
`show_entries` page. If everything worked out well, it will
|
||||
:func:`~flask.flash` an information message to the next request and
|
||||
redirect back to the `show_entries` page::
|
||||
|
||||
|
|
@ -45,8 +46,8 @@ redirect back to the `show_entries` page::
|
|||
flash('New entry was successfully posted')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
||||
Note that we check that the user is logged in here (the `logged_in` key is
|
||||
present in the session and ``True``).
|
||||
Note that this view checks that the user is logged in (that is, if the
|
||||
`logged_in` key is present in the session and ``True``).
|
||||
|
||||
.. admonition:: Security Note
|
||||
|
||||
|
|
@ -81,11 +82,11 @@ notified about that, and the user is asked again::
|
|||
return render_template('login.html', error=error)
|
||||
|
||||
The `logout` function, on the other hand, removes that key from the session
|
||||
again. We use a neat trick here: if you use the :meth:`~dict.pop` method
|
||||
again. There is a neat trick here: if you use the :meth:`~dict.pop` method
|
||||
of the dict and pass a second parameter to it (the default), the method
|
||||
will delete the key from the dictionary if present or do nothing when that
|
||||
key is not in there. This is helpful because now we don't have to check
|
||||
if the user was logged in.
|
||||
key is not in there. This is helpful because now it is not necessary to
|
||||
check if the user was logged in.
|
||||
|
||||
::
|
||||
|
||||
|
|
@ -95,4 +96,23 @@ if the user was logged in.
|
|||
flash('You were logged out')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
||||
.. admonition:: Security Note
|
||||
|
||||
Passwords should never be stored in plain text in a production
|
||||
system. This tutorial uses plain text passwords for simplicity. If you
|
||||
plan to release a project based off this tutorial out into the world,
|
||||
passwords should be both `hashed and salted`_ before being stored in a
|
||||
database or file.
|
||||
|
||||
Fortunately, there are Flask extensions for the purpose of
|
||||
hashing passwords and verifying passwords against hashes, so adding
|
||||
this functionality is fairly straight forward. There are also
|
||||
many general python libraries that can be used for hashing.
|
||||
|
||||
You can find a list of recommended Flask extensions
|
||||
`here <http://flask.pocoo.org/extensions/>`_
|
||||
|
||||
|
||||
Continue with :ref:`tutorial-templates`.
|
||||
|
||||
.. _hashed and salted: https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
|
||||
|
|
|
|||
|
|
@ -14,21 +14,75 @@ This section of the documentation enumerates all the changes in Flask from
|
|||
release to release and how you can change your code to have a painless
|
||||
updating experience.
|
||||
|
||||
If you want to use the :command:`easy_install` command to upgrade your Flask
|
||||
installation, make sure to pass it the :option:`-U` parameter::
|
||||
Use the :command:`pip` command to upgrade your existing Flask installation by
|
||||
providing the ``--upgrade`` parameter::
|
||||
|
||||
$ easy_install -U Flask
|
||||
$ pip install --upgrade Flask
|
||||
|
||||
.. _upgrading-to-10:
|
||||
.. _upgrading-to-012:
|
||||
|
||||
Version 1.0
|
||||
-----------
|
||||
Version 0.12
|
||||
------------
|
||||
|
||||
Flask 1.0 removed the ``debug_log_format`` attribute from Flask
|
||||
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
|
||||
------------
|
||||
|
||||
0.11 is an odd release in the Flask release cycle because it was supposed
|
||||
to be the 1.0 release. However because there was such a long lead time up
|
||||
to the release we decided to push out a 0.11 release first with some
|
||||
changes removed to make the transition easier. If you have been tracking
|
||||
the master branch which was 1.0 you might see some unexpected changes.
|
||||
|
||||
In case you did track the master branch you will notice that :command:`flask --app`
|
||||
is removed now. You need to use the environment variable to specify an
|
||||
application.
|
||||
|
||||
Debugging
|
||||
`````````
|
||||
|
||||
Flask 0.11 removed the ``debug_log_format`` attribute from Flask
|
||||
applications. Instead the new ``LOGGER_HANDLER_POLICY`` configuration can
|
||||
be used to disable the default log handlers and custom log handlers can be
|
||||
set up.
|
||||
|
||||
Error handling
|
||||
``````````````
|
||||
|
||||
The behavior of error handlers was changed.
|
||||
The precedence of handlers used to be based on the decoration/call order of
|
||||
:meth:`~flask.Flask.errorhandler` and
|
||||
|
|
@ -37,14 +91,33 @@ Now the inheritance hierarchy takes precedence and handlers for more
|
|||
specific exception classes are executed instead of more general ones.
|
||||
See :ref:`error-handlers` for specifics.
|
||||
|
||||
Trying to register a handler on an instance now raises :exc:`ValueError`.
|
||||
|
||||
.. note::
|
||||
|
||||
There used to be a logic error allowing you to register handlers
|
||||
only for exception *instances*. This was unintended and plain wrong,
|
||||
and therefore was replaced with the intended behavior of registering
|
||||
handlers only using exception classes and HTTP error codes.
|
||||
|
||||
Trying to register a handler on an instance now raises :exc:`ValueError`.
|
||||
|
||||
Templating
|
||||
``````````
|
||||
|
||||
The :func:`~flask.templating.render_template_string` function has changed to
|
||||
autoescape template variables by default. This better matches the behavior
|
||||
of :func:`~flask.templating.render_template`.
|
||||
|
||||
Extension imports
|
||||
`````````````````
|
||||
|
||||
Extension imports of the form ``flask.ext.foo`` are deprecated, you should use
|
||||
``flask_foo``.
|
||||
|
||||
The old form still works, but Flask will issue a
|
||||
``flask.exthook.ExtDeprecationWarning`` for each extension you import the old
|
||||
way. We also provide a migration utility called `flask-ext-migrate
|
||||
<https://github.com/pallets/flask-ext-migrate>`_ that is supposed to
|
||||
automatically rewrite your imports for this.
|
||||
|
||||
.. _upgrading-to-010:
|
||||
|
||||
|
|
@ -70,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
|
||||
-----------
|
||||
|
|
@ -98,7 +171,7 @@ Version 0.8
|
|||
-----------
|
||||
|
||||
Flask introduced a new session interface system. We also noticed that
|
||||
there was a naming collision between `flask.session` the module that
|
||||
there was a naming collision between ``flask.session`` the module that
|
||||
implements sessions and :data:`flask.session` which is the global session
|
||||
object. With that introduction we moved the implementation details for
|
||||
the session system into a new module called :mod:`flask.sessions`. If you
|
||||
|
|
@ -125,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
|
||||
|
|
@ -142,7 +215,7 @@ good.
|
|||
To apply the upgrade script do the following:
|
||||
|
||||
1. Download the script: `flask-07-upgrade.py
|
||||
<https://raw.githubusercontent.com/mitsuhiko/flask/master/scripts/flask-07-upgrade.py>`_
|
||||
<https://raw.githubusercontent.com/pallets/flask/master/scripts/flask-07-upgrade.py>`_
|
||||
2. Run it in the directory of your application::
|
||||
|
||||
python flask-07-upgrade.py > patchfile.diff
|
||||
|
|
@ -164,7 +237,7 @@ Please note that deprecation warnings are disabled by default starting
|
|||
with Python 2.7. In order to see the deprecation warnings that might be
|
||||
emitted you have to enabled them with the :mod:`warnings` module.
|
||||
|
||||
If you are working with windows and you lack the `patch` command line
|
||||
If you are working with windows and you lack the ``patch`` command line
|
||||
utility you can get it as part of various Unix runtime environments for
|
||||
windows including cygwin, msysgit or ming32. Also source control systems
|
||||
like svn, hg or git have builtin support for applying unified diffs as
|
||||
|
|
@ -181,7 +254,7 @@ before, you should catch them with :exc:`RuntimeError` now.
|
|||
|
||||
Additionally the :func:`~flask.send_file` function is now issuing
|
||||
deprecation warnings if you depend on functionality that will be removed
|
||||
in Flask 1.0. Previously it was possible to use etags and mimetypes
|
||||
in Flask 0.11. Previously it was possible to use etags and mimetypes
|
||||
when file objects were passed. This was unreliable and caused issues
|
||||
for a few setups. If you get a deprecation warning, make sure to
|
||||
update your application to work with either filenames there or disable
|
||||
|
|
@ -281,7 +354,7 @@ to upgrade. What changed?
|
|||
runtime.
|
||||
- Blueprints have an inverse behavior for :meth:`url_for`. Previously
|
||||
``.foo`` told :meth:`url_for` that it should look for the endpoint
|
||||
`foo` on the application. Now it means “relative to current module”.
|
||||
``foo`` on the application. Now it means “relative to current module”.
|
||||
The script will inverse all calls to :meth:`url_for` automatically for
|
||||
you. It will do this in a very eager way so you might end up with
|
||||
some unnecessary leading dots in your code if you're not using
|
||||
|
|
@ -299,7 +372,7 @@ to upgrade. What changed?
|
|||
name into that folder if you want :file:`blueprintname/template.html` as
|
||||
the template name.
|
||||
|
||||
If you continue to use the `Module` object which is deprecated, Flask will
|
||||
If you continue to use the ``Module`` object which is deprecated, Flask will
|
||||
restore the previous behavior as good as possible. However we strongly
|
||||
recommend upgrading to the new blueprints as they provide a lot of useful
|
||||
improvement such as the ability to attach a blueprint multiple times,
|
||||
|
|
@ -319,7 +392,7 @@ change the order.
|
|||
|
||||
Another change that breaks backwards compatibility is that context
|
||||
processors will no longer override values passed directly to the template
|
||||
rendering function. If for example `request` is as variable passed
|
||||
rendering function. If for example ``request`` is as variable passed
|
||||
directly to the template, the default context processor will not override
|
||||
it with the current request object. This makes it easier to extend
|
||||
context processors later to inject additional variables without breaking
|
||||
|
|
@ -345,7 +418,7 @@ The following changes may be relevant to your application:
|
|||
for this feature. Removing support for this makes the Flask internal
|
||||
code easier to understand and fixes a couple of small issues that make
|
||||
debugging harder than necessary.
|
||||
- The `create_jinja_loader` function is gone. If you want to customize
|
||||
- The ``create_jinja_loader`` function is gone. If you want to customize
|
||||
the Jinja loader now, use the
|
||||
:meth:`~flask.Flask.create_jinja_environment` method instead.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue