Merge branch 'master' into vary-cookies
This commit is contained in:
commit
e2f4c0ac16
255 changed files with 11763 additions and 8452 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
CHANGES merge=union
|
||||
2
.github/ISSUE_TEMPLATE.rst
vendored
Normal file
2
.github/ISSUE_TEMPLATE.rst
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
The issue tracker is a tool to address bugs.
|
||||
Please use the #pocoo IRC channel on freenode or Stack Overflow for questions.
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -4,7 +4,16 @@
|
|||
env
|
||||
env*
|
||||
dist
|
||||
build
|
||||
*.egg
|
||||
*.egg-info
|
||||
_mailinglist
|
||||
.tox
|
||||
.cache/
|
||||
.idea/
|
||||
|
||||
# Coverage reports
|
||||
htmlcov
|
||||
.coverage
|
||||
.coverage.*
|
||||
*,cover
|
||||
|
|
|
|||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "docs/_themes"]
|
||||
path = docs/_themes
|
||||
url = git://github.com/mitsuhiko/flask-sphinx-themes.git
|
||||
url = https://github.com/mitsuhiko/flask-sphinx-themes.git
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
git+git://github.com/mitsuhiko/werkzeug.git#egg=Werkzeug
|
||||
git+git://github.com/mitsuhiko/jinja2.git#egg=Jinja2
|
||||
git+git://github.com/mitsuhiko/itsdangerous.git#egg=itsdangerous
|
||||
|
||||
# extra dependencies
|
||||
git+git://github.com/jek/blinker.git#egg=blinker
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
Werkzeug==0.7
|
||||
Jinja2==2.4
|
||||
itsdangerous==0.21
|
||||
|
||||
# extra dependencies
|
||||
blinker==1.0
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# extra dependencies
|
||||
blinker
|
||||
27
.travis.yml
27
.travis.yml
|
|
@ -1,3 +1,4 @@
|
|||
sudo: false
|
||||
language: python
|
||||
|
||||
python:
|
||||
|
|
@ -5,23 +6,43 @@ python:
|
|||
- "2.7"
|
||||
- "pypy"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
|
||||
env:
|
||||
- REQUIREMENTS=lowest
|
||||
- REQUIREMENTS=lowest-simplejson
|
||||
- REQUIREMENTS=release
|
||||
- REQUIREMENTS=release-simplejson
|
||||
- REQUIREMENTS=devel
|
||||
- REQUIREMENTS=devel-simplejson
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
# Python 3 support currently does not work with lowest requirements
|
||||
- python: "3.3"
|
||||
env: REQUIREMENTS=lowest
|
||||
- python: "3.3"
|
||||
env: REQUIREMENTS=lowest-simplejson
|
||||
- python: "3.4"
|
||||
env: REQUIREMENTS=lowest
|
||||
- python: "3.4"
|
||||
env: REQUIREMENTS=lowest-simplejson
|
||||
- python: "3.5"
|
||||
env: REQUIREMENTS=lowest
|
||||
- python: "3.5"
|
||||
env: REQUIREMENTS=lowest-simplejson
|
||||
- python: "3.6"
|
||||
env: REQUIREMENTS=lowest
|
||||
- python: "3.6"
|
||||
env: REQUIREMENTS=lowest-simplejson
|
||||
|
||||
install:
|
||||
- pip install -r .travis-$REQUIREMENTS-requirements.txt
|
||||
- pip install --editable .
|
||||
- pip install tox
|
||||
|
||||
script: make test
|
||||
script:
|
||||
- tox -e py-$REQUIREMENTS
|
||||
|
||||
branches:
|
||||
except:
|
||||
|
|
|
|||
8
AUTHORS
8
AUTHORS
|
|
@ -15,11 +15,18 @@ Patches and Suggestions
|
|||
- Chris Grindstaff
|
||||
- Christopher Grebs
|
||||
- Daniel Neuhäuser
|
||||
- Dan Sully
|
||||
- David Lord @davidism
|
||||
- Edmond Burnett
|
||||
- Florent Xicluna
|
||||
- Georg Brandl
|
||||
- Jeff Widman @jeffwidman
|
||||
- Joshua Bronson @jab
|
||||
- Justin Quick
|
||||
- Kenneth Reitz
|
||||
- Keyan Pishdadian
|
||||
- Marian Sigler
|
||||
- Martijn Pieters
|
||||
- Matt Campell
|
||||
- Matthew Frazier
|
||||
- Michael van Tellingen
|
||||
|
|
@ -29,4 +36,3 @@ Patches and Suggestions
|
|||
- Stephane Wirtel
|
||||
- Thomas Schranz
|
||||
- Zhao Xiaohong
|
||||
- Edmond Burnett
|
||||
|
|
|
|||
209
CHANGES
209
CHANGES
|
|
@ -3,15 +3,128 @@ Flask Changelog
|
|||
|
||||
Here you can see the full list of changes between each Flask release.
|
||||
|
||||
Version 1.0
|
||||
-----------
|
||||
Version 0.13
|
||||
------------
|
||||
|
||||
(release date to be announced, codename to be selected)
|
||||
Major release, unreleased
|
||||
|
||||
- Make `app.run()` into a noop if a Flask application is run from the
|
||||
development server on the command line. This avoids some behavior that
|
||||
was confusing to debug for newcomers.
|
||||
- Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify()
|
||||
method returns compressed response by default, and pretty response in
|
||||
debug mode.
|
||||
- Change Flask.__init__ to accept two new keyword arguments, ``host_matching``
|
||||
and ``static_host``. This enables ``host_matching`` to be set properly by the
|
||||
time the constructor adds the static route, and enables the static route to
|
||||
be properly associated with the required host. (``#1559``)
|
||||
- ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_)
|
||||
- Pass ``_scheme`` argument from ``url_for`` to ``handle_build_error``.
|
||||
(`#2017`_)
|
||||
- Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable
|
||||
adding OPTIONS method when the ``view_func`` argument is not a class.
|
||||
(`#1489`_).
|
||||
- ``MethodView`` can inherit method handlers from base classes. (`#1936`_)
|
||||
- Errors caused while opening the session at the beginning of the request are
|
||||
handled by the app's error handlers. (`#2254`_)
|
||||
- Blueprints gained ``json_encoder`` and ``json_decoder`` attributes to
|
||||
override the app's encoder and decoder. (`#1898`_)
|
||||
- ``Flask.make_response`` raises ``TypeError`` instead of ``ValueError`` for
|
||||
bad response types. The error messages have been improved to describe why the
|
||||
type is invalid. (`#2256`_)
|
||||
- Add ``routes`` CLI command to output routes registered on the application.
|
||||
(`#2259`_)
|
||||
- Show warning when session cookie domain is a bare hostname or an IP
|
||||
address, as these may not behave properly in some browsers, such as Chrome.
|
||||
(`#2282`_)
|
||||
- Allow IP address as exact session cookie domain. (`#2282`_)
|
||||
- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``.
|
||||
(`#2282`_)
|
||||
|
||||
.. _#1489: https://github.com/pallets/flask/pull/1489
|
||||
.. _#1898: https://github.com/pallets/flask/pull/1898
|
||||
.. _#1936: https://github.com/pallets/flask/pull/1936
|
||||
.. _#2017: https://github.com/pallets/flask/pull/2017
|
||||
.. _#2223: https://github.com/pallets/flask/pull/2223
|
||||
.. _#2254: https://github.com/pallets/flask/pull/2254
|
||||
.. _#2256: https://github.com/pallets/flask/pull/2256
|
||||
.. _#2259: https://github.com/pallets/flask/pull/2259
|
||||
.. _#2282: https://github.com/pallets/flask/pull/2282
|
||||
|
||||
Version 0.12.2
|
||||
--------------
|
||||
|
||||
Released on May 16 2017
|
||||
|
||||
- Fix a bug in `safe_join` on Windows.
|
||||
|
||||
Version 0.12.1
|
||||
--------------
|
||||
|
||||
Bugfix release, released on March 31st 2017
|
||||
|
||||
- Prevent `flask run` from showing a NoAppException when an ImportError occurs
|
||||
within the imported application module.
|
||||
- Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix
|
||||
``#2118``.
|
||||
- Use the ``SERVER_NAME`` config if it is present as default values for
|
||||
``app.run``. ``#2109``, ``#2152``
|
||||
- Call `ctx.auto_pop` with the exception object instead of `None`, in the
|
||||
event that a `BaseException` such as `KeyboardInterrupt` is raised in a
|
||||
request handler.
|
||||
|
||||
Version 0.12
|
||||
------------
|
||||
|
||||
Released on December 21st 2016, codename Punsch.
|
||||
|
||||
- the cli command now responds to `--version`.
|
||||
- Mimetype guessing and ETag generation for file-like objects in ``send_file``
|
||||
has been removed, as per issue ``#104``. See pull request ``#1849``.
|
||||
- Mimetype guessing in ``send_file`` now fails loudly and doesn't fall back to
|
||||
``application/octet-stream``. See pull request ``#1988``.
|
||||
- Make ``flask.safe_join`` able to join multiple paths like ``os.path.join``
|
||||
(pull request ``#1730``).
|
||||
- Revert a behavior change that made the dev server crash instead of returning
|
||||
a Internal Server Error (pull request ``#2006``).
|
||||
- Correctly invoke response handlers for both regular request dispatching as
|
||||
well as error handlers.
|
||||
- Disable logger propagation by default for the app logger.
|
||||
- Add support for range requests in ``send_file``.
|
||||
- ``app.test_client`` includes preset default environment, which can now be
|
||||
directly set, instead of per ``client.get``.
|
||||
|
||||
Version 0.11.2
|
||||
--------------
|
||||
|
||||
Bugfix release, unreleased
|
||||
|
||||
- Fix crash when running under PyPy3, see pull request ``#1814``.
|
||||
|
||||
Version 0.11.1
|
||||
--------------
|
||||
|
||||
Bugfix release, released on June 7th 2016.
|
||||
|
||||
- Fixed a bug that prevented ``FLASK_APP=foobar/__init__.py`` from working. See
|
||||
pull request ``#1872``.
|
||||
|
||||
Version 0.11
|
||||
------------
|
||||
|
||||
Released on May 29th 2016, codename Absinthe.
|
||||
|
||||
- Added support to serializing top-level arrays to :func:`flask.jsonify`. This
|
||||
introduces a security risk in ancient browsers. See
|
||||
:ref:`json-security` for details.
|
||||
- Added before_render_template signal.
|
||||
- Added `**kwargs` to :meth:`flask.Test.test_client` to support passing
|
||||
additional keyword arguments to the constructor of
|
||||
:attr:`flask.Flask.test_client_class`.
|
||||
- Added ``SESSION_REFRESH_EACH_REQUEST`` config key that controls the
|
||||
set-cookie behavior. If set to `True` a permanent session will be
|
||||
set-cookie behavior. If set to ``True`` a permanent session will be
|
||||
refreshed each request and get their lifetime extended, if set to
|
||||
`False` it will only be modified if the session actually modifies.
|
||||
``False`` it will only be modified if the session actually modifies.
|
||||
Non permanent sessions are not affected by this and will always
|
||||
expire if the browser window closes.
|
||||
- Made Flask support custom JSON mimetypes for incoming data.
|
||||
|
|
@ -19,13 +132,59 @@ Version 1.0
|
|||
from a view function.
|
||||
- Added :meth:`flask.Config.from_json`.
|
||||
- Added :attr:`flask.Flask.config_class`.
|
||||
- Added :meth:`flask.config.Config.get_namespace`.
|
||||
- Added ``TEMPLATES_AUTO_RELOAD`` config key. If disabled the
|
||||
templates will be reloaded only if the application is running in
|
||||
debug mode. For higher performance it’s possible to disable that.
|
||||
- Added :meth:`flask.Config.get_namespace`.
|
||||
- Templates are no longer automatically reloaded outside of debug mode. This
|
||||
can be configured with the new ``TEMPLATES_AUTO_RELOAD`` config key.
|
||||
- Added a workaround for a limitation in Python 3.3's namespace loader.
|
||||
- Added support for explicit root paths when using Python 3.3's namespace
|
||||
packages.
|
||||
- Added :command:`flask` and the ``flask.cli`` module to start the local
|
||||
debug server through the click CLI system. This is recommended over the old
|
||||
``flask.run()`` method as it works faster and more reliable due to a
|
||||
different design and also replaces ``Flask-Script``.
|
||||
- Error handlers that match specific classes are now checked first,
|
||||
thereby allowing catching exceptions that are subclasses of HTTP
|
||||
exceptions (in ``werkzeug.exceptions``). This makes it possible
|
||||
for an extension author to create exceptions that will by default
|
||||
result in the HTTP error of their choosing, but may be caught with
|
||||
a custom error handler if desired.
|
||||
- Added :meth:`flask.Config.from_mapping`.
|
||||
- Flask will now log by default even if debug is disabled. The log format is
|
||||
now hardcoded but the default log handling can be disabled through the
|
||||
``LOGGER_HANDLER_POLICY`` configuration key.
|
||||
- Removed deprecated module functionality.
|
||||
- Added the ``EXPLAIN_TEMPLATE_LOADING`` config flag which when enabled will
|
||||
instruct Flask to explain how it locates templates. This should help
|
||||
users debug when the wrong templates are loaded.
|
||||
- Enforce blueprint handling in the order they were registered for template
|
||||
loading.
|
||||
- Ported test suite to py.test.
|
||||
- Deprecated ``request.json`` in favour of ``request.get_json()``.
|
||||
- Add "pretty" and "compressed" separators definitions in jsonify() method.
|
||||
Reduces JSON response size when JSONIFY_PRETTYPRINT_REGULAR=False by removing
|
||||
unnecessary white space included by default after separators.
|
||||
- JSON responses are now terminated with a newline character, because it is a
|
||||
convention that UNIX text files end with a newline and some clients don't
|
||||
deal well when this newline is missing. See
|
||||
https://github.com/pallets/flask/pull/1262 -- this came up originally as a
|
||||
part of https://github.com/kennethreitz/httpbin/issues/168
|
||||
- The automatically provided ``OPTIONS`` method is now correctly disabled if
|
||||
the user registered an overriding rule with the lowercase-version
|
||||
``options`` (issue ``#1288``).
|
||||
- ``flask.json.jsonify`` now supports the ``datetime.date`` type (pull request
|
||||
``#1326``).
|
||||
- Don't leak exception info of already catched exceptions to context teardown
|
||||
handlers (pull request ``#1393``).
|
||||
- Allow custom Jinja environment subclasses (pull request ``#1422``).
|
||||
- ``flask.g`` now has ``pop()`` and ``setdefault`` methods.
|
||||
- Turn on autoescape for ``flask.templating.render_template_string`` by default
|
||||
(pull request ``#1515``).
|
||||
- ``flask.ext`` is now deprecated (pull request ``#1484``).
|
||||
- ``send_from_directory`` now raises BadRequest if the filename is invalid on
|
||||
the server OS (pull request ``#1763``).
|
||||
- Added the ``JSONIFY_MIMETYPE`` configuration variable (pull request ``#1728``).
|
||||
- Exceptions during teardown handling will no longer leave bad application
|
||||
contexts lingering around.
|
||||
|
||||
Version 0.10.2
|
||||
--------------
|
||||
|
|
@ -47,6 +206,8 @@ Version 0.10.2
|
|||
- Changed logic of before first request handlers to flip the flag after
|
||||
invoking. This will allow some uses that are potentially dangerous but
|
||||
should probably be permitted.
|
||||
- Fixed Python 3 bug when a handler from `app.url_build_error_handlers`
|
||||
reraises the `BuildError`.
|
||||
|
||||
Version 0.10.1
|
||||
--------------
|
||||
|
|
@ -66,7 +227,7 @@ Version 0.10.1
|
|||
Version 0.10
|
||||
------------
|
||||
|
||||
Released on June 13nd 2013, codename Limoncello.
|
||||
Released on June 13th 2013, codename Limoncello.
|
||||
|
||||
- Changed default cookie serialization format from pickle to JSON to
|
||||
limit the impact an attacker can do if the secret key leaks. See
|
||||
|
|
@ -155,7 +316,7 @@ Released on July 1st 2012, codename Campari.
|
|||
explicitly.
|
||||
- Unregister a circular dependency between the WSGI environment and
|
||||
the request object when shutting down the request. This means that
|
||||
environ ``werkzeug.request`` will be `None` after the response was
|
||||
environ ``werkzeug.request`` will be ``None`` after the response was
|
||||
returned to the WSGI server but has the advantage that the garbage
|
||||
collector is not needed on CPython to tear down the request unless
|
||||
the user created circular dependencies themselves.
|
||||
|
|
@ -176,8 +337,8 @@ Released on July 1st 2012, codename Campari.
|
|||
- The :func:`flask.get_flashed_messages` function now allows rendering flashed
|
||||
message categories in separate blocks, through a ``category_filter``
|
||||
argument.
|
||||
- The :meth:`flask.Flask.run` method now accepts `None` for `host` and `port`
|
||||
arguments, using default values when `None`. This allows for calling run
|
||||
- The :meth:`flask.Flask.run` method now accepts ``None`` for `host` and `port`
|
||||
arguments, using default values when ``None``. This allows for calling run
|
||||
using configuration values, e.g. ``app.run(app.config.get('MYHOST'),
|
||||
app.config.get('MYPORT'))``, with proper behavior whether or not a config
|
||||
file is provided.
|
||||
|
|
@ -246,7 +407,7 @@ Released on September 29th 2011, codename Rakija
|
|||
- Applications now not only have a root path where the resources and modules
|
||||
are located but also an instance path which is the designated place to
|
||||
drop files that are modified at runtime (uploads etc.). Also this is
|
||||
conceptionally only instance depending and outside version control so it's
|
||||
conceptually only instance depending and outside version control so it's
|
||||
the perfect place to put configuration files etc. For more information
|
||||
see :ref:`instance-folders`.
|
||||
- Added the ``APPLICATION_ROOT`` configuration variable.
|
||||
|
|
@ -298,7 +459,7 @@ Released on June 28th 2011, codename Grappa
|
|||
|
||||
- Added :meth:`~flask.Flask.make_default_options_response`
|
||||
which can be used by subclasses to alter the default
|
||||
behavior for `OPTIONS` responses.
|
||||
behavior for ``OPTIONS`` responses.
|
||||
- Unbound locals now raise a proper :exc:`RuntimeError` instead
|
||||
of an :exc:`AttributeError`.
|
||||
- Mimetype guessing and etag support based on file objects is now
|
||||
|
|
@ -311,10 +472,10 @@ Released on June 28th 2011, codename Grappa
|
|||
1.0 the old behavior will continue to work but issue dependency
|
||||
warnings.
|
||||
- fixed a problem for Flask to run on jython.
|
||||
- added a `PROPAGATE_EXCEPTIONS` configuration variable that can be
|
||||
- added a ``PROPAGATE_EXCEPTIONS`` configuration variable that can be
|
||||
used to flip the setting of exception propagation which previously
|
||||
was linked to `DEBUG` alone and is now linked to either `DEBUG` or
|
||||
`TESTING`.
|
||||
was linked to ``DEBUG`` alone and is now linked to either ``DEBUG`` or
|
||||
``TESTING``.
|
||||
- Flask no longer internally depends on rules being added through the
|
||||
`add_url_rule` function and can now also accept regular werkzeug
|
||||
rules added to the url map.
|
||||
|
|
@ -351,8 +512,8 @@ Version 0.6.1
|
|||
|
||||
Bugfix release, released on December 31st 2010
|
||||
|
||||
- Fixed an issue where the default `OPTIONS` response was
|
||||
not exposing all valid methods in the `Allow` header.
|
||||
- Fixed an issue where the default ``OPTIONS`` response was
|
||||
not exposing all valid methods in the ``Allow`` header.
|
||||
- Jinja2 template loading syntax now allows "./" in front of
|
||||
a template load path. Previously this caused issues with
|
||||
module setups.
|
||||
|
|
@ -397,7 +558,7 @@ Released on July 27th 2010, codename Whisky
|
|||
prefix. This makes it possible to bind a whole module to a
|
||||
configurable subdomain.
|
||||
|
||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||
.. _blinker: https://pypi.python.org/pypi/blinker
|
||||
|
||||
Version 0.5.2
|
||||
-------------
|
||||
|
|
@ -422,7 +583,7 @@ Released on July 6th 2010, codename Calvados
|
|||
|
||||
- fixed a bug with subdomains that was caused by the inability to
|
||||
specify the server name. The server name can now be set with
|
||||
the `SERVER_NAME` config key. This key is now also used to set
|
||||
the ``SERVER_NAME`` config key. This key is now also used to set
|
||||
the session cookie cross-subdomain wide.
|
||||
- autoescaping is no longer active for all templates. Instead it
|
||||
is only active for ``.html``, ``.htm``, ``.xml`` and ``.xhtml``.
|
||||
|
|
@ -454,8 +615,8 @@ Released on June 18th 2010, codename Rakia
|
|||
requests that do not pop the request stack for testing.
|
||||
- because the Python standard library caches loggers, the name of
|
||||
the logger is configurable now to better support unittests.
|
||||
- added `TESTING` switch that can activate unittesting helpers.
|
||||
- the logger switches to `DEBUG` mode now if debug is enabled.
|
||||
- added ``TESTING`` switch that can activate unittesting helpers.
|
||||
- the logger switches to ``DEBUG`` mode now if debug is enabled.
|
||||
|
||||
Version 0.3.1
|
||||
-------------
|
||||
|
|
|
|||
114
CONTRIBUTING.rst
Normal file
114
CONTRIBUTING.rst
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
==========================
|
||||
How to contribute to Flask
|
||||
==========================
|
||||
|
||||
Thanks for considering contributing to Flask.
|
||||
|
||||
Support questions
|
||||
=================
|
||||
|
||||
Please, don't use the issue tracker for this. Check whether the ``#pocoo`` IRC
|
||||
channel on Freenode can help with your issue. If your problem is not strictly
|
||||
Werkzeug or Flask specific, ``#python`` is generally more active.
|
||||
`Stack Overflow <https://stackoverflow.com/>`_ is also worth considering.
|
||||
|
||||
Reporting issues
|
||||
================
|
||||
|
||||
- Under which versions of Python does this happen? This is even more important
|
||||
if your issue is encoding related.
|
||||
|
||||
- Under which versions of Werkzeug does this happen? Check if this issue is
|
||||
fixed in the repository.
|
||||
|
||||
Submitting patches
|
||||
==================
|
||||
|
||||
- Include tests if your patch is supposed to solve a bug, and explain
|
||||
clearly under which circumstances the bug happens. Make sure the test fails
|
||||
without your patch.
|
||||
|
||||
- Try to follow `PEP8 <https://www.python.org/dev/peps/pep-0008/>`_, but you
|
||||
may ignore the line-length-limit if following it would make the code uglier.
|
||||
|
||||
|
||||
Running the testsuite
|
||||
---------------------
|
||||
|
||||
You probably want to set up a `virtualenv
|
||||
<https://virtualenv.readthedocs.io/en/latest/index.html>`_.
|
||||
|
||||
The minimal requirement for running the testsuite is ``pytest``. You can
|
||||
install it with::
|
||||
|
||||
pip install pytest
|
||||
|
||||
Clone this repository::
|
||||
|
||||
git clone https://github.com/pallets/flask.git
|
||||
|
||||
Install Flask as an editable package using the current source::
|
||||
|
||||
cd flask
|
||||
pip install --editable .
|
||||
|
||||
Then you can run the testsuite with::
|
||||
|
||||
pytest tests/
|
||||
|
||||
With only pytest installed, a large part of the testsuite will get skipped
|
||||
though. Whether this is relevant depends on which part of Flask you're working
|
||||
on. Travis is set up to run the full testsuite when you submit your pull
|
||||
request anyways.
|
||||
|
||||
If you really want to test everything, you will have to install ``tox`` instead
|
||||
of ``pytest``. You can install it with::
|
||||
|
||||
pip install tox
|
||||
|
||||
The ``tox`` command will then run all tests against multiple combinations
|
||||
Python versions and dependency versions.
|
||||
|
||||
Running test coverage
|
||||
---------------------
|
||||
Generating a report of lines that do not have unit test coverage can indicate where
|
||||
to start contributing. ``pytest`` integrates with ``coverage.py``, using the ``pytest-cov``
|
||||
plugin. This assumes you have already run the testsuite (see previous section)::
|
||||
|
||||
pip install pytest-cov
|
||||
|
||||
After this has been installed, you can output a report to the command line using this command::
|
||||
|
||||
pytest --cov=flask tests/
|
||||
|
||||
Generate a HTML report can be done using this command::
|
||||
|
||||
pytest --cov-report html --cov=flask tests/
|
||||
|
||||
Full docs on ``coverage.py`` are here: https://coverage.readthedocs.io
|
||||
|
||||
Caution
|
||||
=======
|
||||
pushing
|
||||
-------
|
||||
This repository contains several zero-padded file modes that may cause issues when pushing this repository to git hosts other than github. Fixing this is destructive to the commit history, so we suggest ignoring these warnings. If it fails to push and you're using a self-hosted git service like Gitlab, you can turn off repository checks in the admin panel.
|
||||
|
||||
|
||||
cloning
|
||||
-------
|
||||
The zero-padded file modes files above can cause issues while cloning, too. If you have
|
||||
|
||||
::
|
||||
|
||||
[fetch]
|
||||
fsckobjects = true
|
||||
|
||||
or
|
||||
|
||||
::
|
||||
|
||||
[receive]
|
||||
fsckObjects = true
|
||||
|
||||
|
||||
set in your git configuration file, cloning this repository will fail. The only solution is to set both of the above settings to false while cloning, and then setting them back to true after the cloning is finished.
|
||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2014 by Armin Ronacher and contributors. See AUTHORS
|
||||
Copyright (c) 2015 by Armin Ronacher and contributors. See AUTHORS
|
||||
for more details.
|
||||
|
||||
Some rights reserved.
|
||||
|
|
|
|||
25
MANIFEST.in
25
MANIFEST.in
|
|
@ -1,16 +1,11 @@
|
|||
include Makefile CHANGES LICENSE AUTHORS run-tests.py
|
||||
recursive-include artwork *
|
||||
recursive-include tests *
|
||||
recursive-include examples *
|
||||
recursive-include docs *
|
||||
recursive-exclude docs *.pyc
|
||||
recursive-exclude docs *.pyo
|
||||
recursive-exclude tests *.pyc
|
||||
recursive-exclude tests *.pyo
|
||||
recursive-exclude examples *.pyc
|
||||
recursive-exclude examples *.pyo
|
||||
recursive-include flask/testsuite/static *
|
||||
recursive-include flask/testsuite/templates *
|
||||
recursive-include flask/testsuite/test_apps *
|
||||
include Makefile CHANGES LICENSE AUTHORS
|
||||
|
||||
graft artwork
|
||||
graft tests
|
||||
graft examples
|
||||
graft docs
|
||||
|
||||
global-exclude *.py[co]
|
||||
|
||||
prune docs/_build
|
||||
prune docs/_themes/.git
|
||||
prune docs/_themes
|
||||
|
|
|
|||
9
Makefile
9
Makefile
|
|
@ -3,13 +3,8 @@
|
|||
all: clean-pyc test
|
||||
|
||||
test:
|
||||
python run-tests.py
|
||||
|
||||
tox-test:
|
||||
tox
|
||||
|
||||
test-with-mem:
|
||||
RUN_FLASK_MEMORY_TESTS=1 python run-tests.py
|
||||
pip install -r test-requirements.txt
|
||||
tox -e py-release
|
||||
|
||||
audit:
|
||||
python setup.py audit
|
||||
|
|
|
|||
24
README
24
README
|
|
@ -19,11 +19,9 @@
|
|||
|
||||
~ What do I need?
|
||||
|
||||
Jinja 2.4 and Werkzeug 0.7 or later.
|
||||
`pip` or `easy_install` will install them for you if you do
|
||||
`pip install Flask`. I encourage you to use a virtualenv.
|
||||
Check the docs for complete installation and usage
|
||||
instructions.
|
||||
All dependencies are installed by using `pip install Flask`.
|
||||
We encourage you to use a virtualenv. Check the docs for
|
||||
complete installation and usage instructions.
|
||||
|
||||
~ Where are the docs?
|
||||
|
||||
|
|
@ -34,20 +32,12 @@
|
|||
~ Where are the tests?
|
||||
|
||||
Good that you're asking. The tests are in the
|
||||
flask/testsuite package. To run the tests use the
|
||||
`run-tests.py` file:
|
||||
tests/ folder. To run the tests use the
|
||||
`pytest` testing tool:
|
||||
|
||||
$ python run-tests.py
|
||||
$ pytest
|
||||
|
||||
If it's not enough output for you, you can use the
|
||||
`--verbose` flag:
|
||||
|
||||
$ python run-tests.py --verbose
|
||||
|
||||
If you just want one particular testcase to run you can
|
||||
provide it on the command line:
|
||||
|
||||
$ python run-tests.py test_to_run
|
||||
Details on contributing can be found in CONTRIBUTING.rst
|
||||
|
||||
~ Where can I get help?
|
||||
|
||||
|
|
|
|||
165
artwork/logo-lineart.svg
Normal file
165
artwork/logo-lineart.svg
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="211.15901"
|
||||
height="190.52811"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="logo-lineart.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<inkscape:perspective
|
||||
id="perspective2824"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2840"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2878"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2894"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2910"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2926"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2976"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3020"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3036"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3052"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3866"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.1723341"
|
||||
inkscape:cx="242.05817"
|
||||
inkscape:cy="92.686293"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1676"
|
||||
inkscape:window-height="1005"
|
||||
inkscape:window-x="4"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="10"
|
||||
fit-margin-left="10"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="10" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-29.820801,-20.186869)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="M 9.7525867,55.788422 C 40.421293,45.982204 33.821969,46.567748 69.327984,40.346648 c 8.493721,2.411576 22.910914,5.687215 22.240236,12.296506 -6.241933,2.320572 -15.351869,-6.455434 -20.254712,-1.539882 0.01014,18.421641 5.965221,38.200493 13.480678,55.747588 7.515457,17.5471 18.880714,32.86245 34.290034,42.35708 20.42595,12.66826 41.92048,14.9356 63.64846,15.65546 6.66858,0.23786 17.30912,-1.47838 20.01846,0 -4.9124,8.703 -19.28006,12.8118 -34.21844,14.71154 -14.93837,1.89974 -30.44747,1.59043 -37.64272,1.45723 -15.88921,-0.50065 -29.5942,-2.65111 -42.06658,-7.29048 C 56.640409,160.78176 38.428746,134.71246 24.668078,106.25832 16.765019,89.693325 11.290118,72.259923 9.7525867,55.788422 z"
|
||||
id="path3826"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(29.820801,20.186869)"
|
||||
sodipodi:nodetypes="ccccscccscccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 54.066806,32.351647 c -0.427165,0.87404 -0.384822,1.998232 -0.02834,2.90275 0.781834,1.983761 2.799883,3.252081 4.397491,4.681241 0.728446,0.651642 2.26934,0.803097 2.364296,1.769134 0.215279,2.190161 -2.700769,3.566537 -4.456242,4.921486 -1.316317,1.015991 -3.845581,0.776849 -4.451985,2.314219 -0.417515,1.058499 0.837317,2.10047 1.1679,3.188615 0.465799,1.533243 1.642442,3.150334 1.145997,4.674061 -0.597449,1.833868 -2.700081,2.84663 -4.420626,3.754433 -1.893115,0.998854 -4.450538,0.497797 -6.207667,1.715064 -1.674125,1.159765 -3.485979,2.907099 -3.554321,4.925579 -0.03097,0.915115 -0.384582,2.676814 -0.233936,3.114037 12.863193,-4.155671 20.195138,-6.507915 28.694286,-8.598094 8.499136,-2.090222 16.108852,-3.399531 29.579722,-5.689662 -0.06867,-0.457321 -1.197061,-1.855664 -1.647827,-2.652661 -0.994254,-1.75795 -3.408869,-2.469029 -5.429591,-2.722885 -2.120906,-0.26644 -4.15652,1.360749 -6.296964,1.350851 -1.945327,-0.009 -4.277958,0.06569 -5.655921,-1.283841 -1.144955,-1.121286 -0.849755,-3.099246 -1.145997,-4.674061 -0.210243,-1.117649 0.420309,-2.621884 -0.439473,-3.367212 -1.248754,-1.082519 -3.380554,0.299434 -5.017542,0.0075 -2.183125,-0.389274 -5.405114,-0.260713 -6.227327,-2.302063 -0.362663,-0.900401 0.93342,-1.747432 1.277831,-2.662119 0.75535,-2.006065 1.957858,-4.064009 1.733419,-6.184432 -0.102333,-0.966833 -0.5848,-1.983113 -1.367813,-2.56044 -1.68203,-1.191313 -4.366912,-1.323763 -7.531636,-0.525881 -3.164723,0.797885 -5.342193,2.137743 -6.247739,3.90434 z"
|
||||
id="path3832"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csssssscssscccssscssssssccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 71.836704,50.617573 c 0,0 -24.55635,5.277975 -35.918352,8.551988 C 27.8621,61.491007 12.143824,67.37947 12.143824,67.37947"
|
||||
id="path3847"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(29.820801,20.186869)"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
6
docs/_templates/sidebarintro.html
vendored
6
docs/_templates/sidebarintro.html
vendored
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Thread-Locals in Flask
|
|||
|
||||
One of the design decisions in Flask was that simple tasks should be simple;
|
||||
they should not take a lot of code and yet they should not limit you. Because
|
||||
of that, Flask has few design choices that some people might find surprising or
|
||||
of that, Flask has a few design choices that some people might find surprising or
|
||||
unorthodox. For example, Flask uses thread-local objects internally so that you
|
||||
don’t have to pass objects around from function to function within a request in
|
||||
order to stay threadsafe. This approach is convenient, but requires a valid
|
||||
|
|
@ -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.6 and 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`.
|
||||
|
|
|
|||
300
docs/api.rst
300
docs/api.rst
|
|
@ -30,96 +30,43 @@ 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
|
||||
.. attribute:: url
|
||||
.. attribute:: base_url
|
||||
.. attribute:: url_root
|
||||
|
||||
Provides different ways to look at the current URL. Imagine your
|
||||
application is listening on the following URL::
|
||||
Provides different ways to look at the current `IRI
|
||||
<http://tools.ietf.org/html/rfc3987>`_. Imagine your application is
|
||||
listening on the following application root::
|
||||
|
||||
http://www.example.com/myapplication
|
||||
|
||||
And a user requests the following URL::
|
||||
And a user requests the following URI::
|
||||
|
||||
http://www.example.com/myapplication/page.html?x=y
|
||||
http://www.example.com/myapplication/%CF%80/page.html?x=y
|
||||
|
||||
In this case the values of the above mentioned attributes would be
|
||||
the following:
|
||||
|
||||
============= ======================================================
|
||||
`path` ``/page.html``
|
||||
`script_root` ``/myapplication``
|
||||
`base_url` ``http://www.example.com/myapplication/page.html``
|
||||
`url` ``http://www.example.com/myapplication/page.html?x=y``
|
||||
`url_root` ``http://www.example.com/myapplication/``
|
||||
`path` ``u'/π/page.html'``
|
||||
`full_path` ``u'/π/page.html?x=y'``
|
||||
`script_root` ``u'/myapplication'``
|
||||
`base_url` ``u'http://www.example.com/myapplication/π/page.html'``
|
||||
`url` ``u'http://www.example.com/myapplication/π/page.html?x=y'``
|
||||
`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
|
||||
|
|
@ -142,7 +89,7 @@ Response Objects
|
|||
|
||||
.. attribute:: headers
|
||||
|
||||
A :class:`Headers` object representing the response headers.
|
||||
A :class:`~werkzeug.datastructures.Headers` object representing the response headers.
|
||||
|
||||
.. attribute:: status
|
||||
|
||||
|
|
@ -176,14 +123,14 @@ To access the current session you can use the :class:`session` object:
|
|||
|
||||
.. attribute:: new
|
||||
|
||||
`True` if the session is new, `False` otherwise.
|
||||
``True`` if the session is new, ``False`` otherwise.
|
||||
|
||||
.. attribute:: modified
|
||||
|
||||
`True` if the session object detected a modification. Be advised
|
||||
``True`` if the session object detected a modification. Be advised
|
||||
that modifications on mutable structures are not picked up
|
||||
automatically, in that situation you have to explicitly set the
|
||||
attribute to `True` yourself. Here an example::
|
||||
attribute to ``True`` yourself. Here an example::
|
||||
|
||||
# this change is not picked up because a mutable object (here
|
||||
# a list) is changed.
|
||||
|
|
@ -193,9 +140,9 @@ To access the current session you can use the :class:`session` object:
|
|||
|
||||
.. attribute:: permanent
|
||||
|
||||
If set to `True` the session lives for
|
||||
If set to ``True`` the session lives for
|
||||
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
|
||||
default is 31 days. If set to `False` (which is the default) the
|
||||
default is 31 days. If set to ``False`` (which is the default) the
|
||||
session will be deleted when the user closes the browser.
|
||||
|
||||
|
||||
|
|
@ -277,7 +224,7 @@ thing, like it does for :class:`request` and :class:`session`.
|
|||
pattern for testing.
|
||||
|
||||
Additionally as of 0.10 you can use the :meth:`get` method to
|
||||
get an attribute or `None` (or the second argument) if it's not set.
|
||||
get an attribute or ``None`` (or the second argument) if it's not set.
|
||||
These two usages are now equivalent::
|
||||
|
||||
user = getattr(flask.g, 'user', None)
|
||||
|
|
@ -286,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.
|
||||
|
||||
|
||||
|
|
@ -310,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
|
||||
|
||||
|
|
@ -348,7 +292,7 @@ JSON Support
|
|||
.. module:: flask.json
|
||||
|
||||
Flask uses ``simplejson`` for the JSON implementation. Since simplejson
|
||||
is provided both by the standard library as well as extension Flask will
|
||||
is provided by both the standard library as well as extension, Flask will
|
||||
try simplejson first and then fall back to the stdlib json module. On top
|
||||
of that it will delegate access to the current application's JSON encoders
|
||||
and decoders for easier customization.
|
||||
|
|
@ -374,9 +318,9 @@ JSON module:
|
|||
as string.
|
||||
|
||||
The :func:`~htmlsafe_dumps` function of this json module is also available
|
||||
as filter called ``|tojson`` in Jinja2. Note that inside `script`
|
||||
as filter called ``|tojson`` in Jinja2. Note that inside ``script``
|
||||
tags no escaping must take place, so make sure to disable escaping
|
||||
with ``|safe`` if you intend to use it inside `script` tags unless
|
||||
with ``|safe`` if you intend to use it inside ``script`` tags unless
|
||||
you are using Flask 0.10 which implies that:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
|
@ -505,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
|
||||
``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.
|
||||
|
|
@ -542,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
|
||||
|
||||
|
|
@ -573,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
|
||||
|
|
@ -597,7 +646,9 @@ Signals
|
|||
do nothing but will fail with a :exc:`RuntimeError` for all other
|
||||
operations, including connecting.
|
||||
|
||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||
|
||||
.. _blinker: https://pypi.python.org/pypi/blinker
|
||||
|
||||
|
||||
Class-Based Views
|
||||
-----------------
|
||||
|
|
@ -638,8 +689,12 @@ 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`.
|
||||
|
||||
Here are some examples::
|
||||
|
||||
@app.route('/')
|
||||
|
|
@ -702,9 +757,9 @@ instead of the `view_func` parameter.
|
|||
`**options` the options to be forwarded to the underlying
|
||||
:class:`~werkzeug.routing.Rule` object. A change to
|
||||
Werkzeug is handling of method options. methods is a list
|
||||
of methods this rule should be limited to (`GET`, `POST`
|
||||
etc.). By default a rule just listens for `GET` (and
|
||||
implicitly `HEAD`). Starting with Flask 0.6, `OPTIONS` is
|
||||
of methods this rule should be limited to (``GET``, ``POST``
|
||||
etc.). By default a rule just listens for ``GET`` (and
|
||||
implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
|
||||
implicitly added and handled by the standard request
|
||||
handling. They have to be specified as keyword arguments.
|
||||
=============== ==========================================================
|
||||
|
|
@ -725,14 +780,14 @@ some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior:
|
|||
cannot be customized from the function itself.
|
||||
|
||||
- `methods`: If methods are not provided when the URL rule is added,
|
||||
Flask will look on the view function object itself is an `methods`
|
||||
Flask will look on the view function object itself if a `methods`
|
||||
attribute exists. If it does, it will pull the information for the
|
||||
methods from there.
|
||||
|
||||
- `provide_automatic_options`: if this attribute is set Flask will
|
||||
either force enable or disable the automatic implementation of the
|
||||
HTTP `OPTIONS` response. This can be useful when working with
|
||||
decorators that want to customize the `OPTIONS` response on a per-view
|
||||
HTTP ``OPTIONS`` response. This can be useful when working with
|
||||
decorators that want to customize the ``OPTIONS`` response on a per-view
|
||||
basis.
|
||||
|
||||
- `required_methods`: if this attribute is set, Flask will always add
|
||||
|
|
@ -753,3 +808,28 @@ Full example::
|
|||
|
||||
.. versionadded:: 0.8
|
||||
The `provide_automatic_options` functionality was added.
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
.. currentmodule:: flask.cli
|
||||
|
||||
.. autoclass:: FlaskGroup
|
||||
:members:
|
||||
|
||||
.. autoclass:: AppGroup
|
||||
:members:
|
||||
|
||||
.. autoclass:: ScriptInfo
|
||||
:members:
|
||||
|
||||
.. autofunction:: with_appcontext
|
||||
|
||||
.. autofunction:: pass_script_info
|
||||
|
||||
Marks a function so that an instance of :class:`ScriptInfo` is passed
|
||||
as first argument to the click callback.
|
||||
|
||||
.. 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.
|
||||
|
||||
|
|
@ -38,8 +38,8 @@ Purpose of the Application Context
|
|||
----------------------------------
|
||||
|
||||
The main reason for the application's context existence is that in the
|
||||
past a bunch of functionality was attached to the request context in lack
|
||||
of a better solution. Since one of the pillar's of Flask's design is that
|
||||
past a bunch of functionality was attached to the request context for lack
|
||||
of a better solution. Since one of the pillars of Flask's design is that
|
||||
you can have more than one application in the same Python process.
|
||||
|
||||
So how does the code find the “right” application? In the past we
|
||||
|
|
@ -48,17 +48,17 @@ with libraries that were not designed with that in mind.
|
|||
|
||||
A common workaround for that problem was to use the
|
||||
:data:`~flask.current_app` proxy later on, which was bound to the current
|
||||
request's application reference. Since however creating such a request
|
||||
context is an unnecessarily expensive operation in case there is no
|
||||
request around, the application context was introduced.
|
||||
request's application reference. Since creating such a request context is
|
||||
an unnecessarily expensive operation in case there is no request around,
|
||||
the application context was introduced.
|
||||
|
||||
Creating an Application Context
|
||||
-------------------------------
|
||||
|
||||
To make an application context there are two ways. The first one is the
|
||||
implicit one: whenever a request context is pushed, an application context
|
||||
will be created alongside if this is necessary. As a result of that, you
|
||||
can ignore the existence of the application context unless you need it.
|
||||
There are two ways to make an application context. The first one is
|
||||
implicit: whenever a request context is pushed, an application context
|
||||
will be created alongside if this is necessary. As a result, you can
|
||||
ignore the existence of the application context unless you need it.
|
||||
|
||||
The second way is the explicit way using the
|
||||
:meth:`~flask.Flask.app_context` method::
|
||||
|
|
@ -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
|
||||
-----------------------
|
||||
|
||||
|
|
@ -91,9 +96,9 @@ For more information about that, see :ref:`extension-dev`.
|
|||
Context Usage
|
||||
-------------
|
||||
|
||||
The context is typically used to cache resources on there that need to be
|
||||
created on a per-request or usage case. For instance database connects
|
||||
are destined to go there. When storing things on the application context
|
||||
The context is typically used to cache resources that need to be created
|
||||
on a per-request or usage case. For instance, database connections are
|
||||
destined to go there. When storing things on the application context
|
||||
unique names should be chosen as this is a place that is shared between
|
||||
Flask applications and extensions.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 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.
|
||||
---------------------
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
Modular Applications with Blueprints
|
||||
====================================
|
||||
|
||||
.. currentmodule:: flask
|
||||
|
||||
.. versionadded:: 0.7
|
||||
|
||||
Flask uses a concept of *blueprints* for making application components and
|
||||
|
|
@ -97,7 +99,7 @@ these::
|
|||
<Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
|
||||
<Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]
|
||||
|
||||
The first one is obviously from the application ifself for the static
|
||||
The first one is obviously from the application itself for the static
|
||||
files. The other two are for the `show` function of the ``simple_page``
|
||||
blueprint. As you can see, they are also prefixed with the name of the
|
||||
blueprint and separated by a dot (``.``).
|
||||
|
|
@ -157,7 +159,7 @@ blueprint::
|
|||
admin = Blueprint('admin', __name__, static_folder='static')
|
||||
|
||||
By default the rightmost part of the path is where it is exposed on the
|
||||
web. Because the folder is called ``static`` here it will be available at
|
||||
web. Because the folder is called :file:`static` here it will be available at
|
||||
the location of the blueprint + ``/static``. Say the blueprint is
|
||||
registered for ``/admin`` the static folder will be at ``/admin/static``.
|
||||
|
||||
|
|
@ -174,16 +176,43 @@ 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: ``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
|
||||
blueprint, the best idea is to lay out your templates like this::
|
||||
|
||||
yourpackage/
|
||||
blueprints/
|
||||
admin/
|
||||
templates/
|
||||
admin/
|
||||
index.html
|
||||
__init__.py
|
||||
|
||||
And then when you want to render the template, use :file:`admin/index.html` as
|
||||
the name to look up the template by. If you encounter problems loading
|
||||
the correct templates enable the ``EXPLAIN_TEMPLATE_LOADING`` config
|
||||
variable which will instruct Flask to print out the steps it goes through
|
||||
to locate templates on every ``render_template`` call.
|
||||
|
||||
Building URLs
|
||||
-------------
|
||||
|
|
@ -216,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`.
|
||||
|
|
|
|||
260
docs/cli.rst
Normal file
260
docs/cli.rst
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
.. _cli:
|
||||
|
||||
Command Line Interface
|
||||
======================
|
||||
|
||||
.. versionadded:: 0.11
|
||||
|
||||
.. currentmodule:: flask
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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. 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. 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.
|
||||
|
||||
Environment variables (On Windows use ``set`` instead of ``export``)::
|
||||
|
||||
export FLASK_APP=hello
|
||||
flask run
|
||||
|
||||
Or with a filename::
|
||||
|
||||
export FLASK_APP=/path/to/hello.py
|
||||
flask run
|
||||
|
||||
Virtualenv Integration
|
||||
----------------------
|
||||
|
||||
If you are constantly working with a virtualenv you can also put the
|
||||
``export FLASK_APP`` into your ``activate`` script by adding it to 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 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 shell
|
||||
|
||||
This will start up an interactive Python shell, setup the correct
|
||||
application context and setup the local variables in the shell. This is
|
||||
done by invoking the :meth:`Flask.make_shell_context` method of the
|
||||
application. By default you have access to your ``app`` and :data:`g`.
|
||||
|
||||
Custom Commands
|
||||
---------------
|
||||
|
||||
If you want to add more commands to the shell script you can do this
|
||||
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__)
|
||||
|
||||
@app.cli.command()
|
||||
def initdb():
|
||||
"""Initialize the database."""
|
||||
click.echo('Init the db')
|
||||
|
||||
The command will then show up on the command line::
|
||||
|
||||
$ flask initdb
|
||||
Init the db
|
||||
|
||||
Application Context
|
||||
-------------------
|
||||
|
||||
Most commands operate on the application so it makes a lot of sense if
|
||||
they have the application context setup. Because of this, if you register
|
||||
a callback on ``app.cli`` with the :meth:`~flask.cli.AppGroup.command` the
|
||||
callback will automatically be wrapped through :func:`cli.with_appcontext`
|
||||
which informs the cli system to ensure that an application context is set
|
||||
up. This behavior is not available if a command is added later with
|
||||
:func:`~click.Group.add_command` or through other means.
|
||||
|
||||
It can also be disabled by passing ``with_appcontext=False`` to the
|
||||
decorator::
|
||||
|
||||
@app.cli.command(with_appcontext=False)
|
||||
def example():
|
||||
pass
|
||||
|
||||
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
|
||||
instantiate your application properly by itself. Because of this reason
|
||||
the recommendation is to create a separate file that instantiates
|
||||
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
|
||||
application from an environment variable.
|
||||
|
||||
This could be a file named :file:`autoapp.py` with these contents::
|
||||
|
||||
import os
|
||||
from yourapplication import create_app
|
||||
app = create_app(os.environ['YOURAPPLICATION_CONFIG'])
|
||||
|
||||
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
|
||||
|
||||
From this point onwards :command:`flask` will find your application.
|
||||
|
||||
.. _custom-scripts:
|
||||
|
||||
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
|
||||
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
|
||||
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`).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
def create_wiki_app(info):
|
||||
from yourwiki import create_app
|
||||
return create_app(
|
||||
config=os.environ.get('WIKI_CONFIG', 'wikiconfig.py'))
|
||||
|
||||
@click.group(cls=FlaskGroup, create_app=create_wiki_app)
|
||||
def cli():
|
||||
"""This is a management script for the wiki application."""
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
|
||||
That's a lot of code for not much, so let's go through all parts step by
|
||||
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.
|
||||
2. The next thing we do is defining a function that is invoked with the
|
||||
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`). 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. All is rounded up by invoking the script.
|
||||
|
||||
CLI Plugins
|
||||
-----------
|
||||
|
||||
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:
|
||||
|
||||
Example :file:`setup.py`::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='flask-my-extension',
|
||||
...
|
||||
entry_points='''
|
||||
[flask.commands]
|
||||
my-command=mypackage.commands:cli
|
||||
''',
|
||||
)
|
||||
|
||||
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.
|
||||
133
docs/conf.py
133
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'2014, 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,26 +264,47 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
|
|||
#epub_tocdepth = 3
|
||||
|
||||
intersphinx_mapping = {
|
||||
'http://docs.python.org/dev': None,
|
||||
'http://werkzeug.pocoo.org/docs/': None,
|
||||
'http://www.sqlalchemy.org/docs/': None,
|
||||
'http://wtforms.simplecodes.com/docs/0.5/': None,
|
||||
'http://discorporate.us/projects/Blinker/docs/1.1/': 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 = '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)
|
||||
|
||||
pygments_style = 'tango'
|
||||
html_theme = 'default'
|
||||
html_theme_options = {}
|
||||
|
||||
# unwrap decorators
|
||||
def unwrap_decorators():
|
||||
import sphinx.util.inspect as inspect
|
||||
import functools
|
||||
|
||||
old_getargspec = inspect.getargspec
|
||||
def getargspec(x):
|
||||
return old_getargspec(getattr(x, '_original_function', x))
|
||||
inspect.getargspec = getargspec
|
||||
|
||||
old_update_wrapper = functools.update_wrapper
|
||||
def update_wrapper(wrapper, wrapped, *a, **kw):
|
||||
rv = old_update_wrapper(wrapper, wrapped, *a, **kw)
|
||||
rv._original_function = wrapped
|
||||
return rv
|
||||
functools.update_wrapper = update_wrapper
|
||||
|
||||
unwrap_decorators()
|
||||
del unwrap_decorators
|
||||
|
|
|
|||
115
docs/config.rst
115
docs/config.rst
|
|
@ -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,13 +67,14 @@ 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
|
||||
explicitly set to `None` this is
|
||||
implicitly true if either `TESTING` or
|
||||
`DEBUG` is true.
|
||||
explicitly set to ``None`` this is
|
||||
implicitly true if either ``TESTING`` or
|
||||
``DEBUG`` is true.
|
||||
``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in
|
||||
debug mode the request context is not
|
||||
popped on exceptions to enable debuggers
|
||||
|
|
@ -80,25 +96,33 @@ The following configuration values are used internally by Flask:
|
|||
that is not set for ``'/'``.
|
||||
``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set
|
||||
with the httponly flag. Defaults to
|
||||
`True`.
|
||||
``True``.
|
||||
``SESSION_COOKIE_SECURE`` controls if the cookie should be set
|
||||
with the secure flag. Defaults to
|
||||
`False`.
|
||||
``False``.
|
||||
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
|
||||
:class:`datetime.timedelta` object.
|
||||
Starting with Flask 0.8 this can also be
|
||||
an integer representing seconds.
|
||||
``SESSION_REFRESH_EACH_REQUEST`` this flag controls how permanent
|
||||
sessions are refreshed. If set to `True`
|
||||
sessions are refreshed. If set to ``True``
|
||||
(which is the default) then the cookie
|
||||
is refreshed each request which
|
||||
automatically bumps the lifetime. If
|
||||
set to `False` a `set-cookie` header is
|
||||
set to ``False`` a `set-cookie` header is
|
||||
only sent if the session is modified.
|
||||
Non permanent sessions are not affected
|
||||
by this.
|
||||
``USE_X_SENDFILE`` enable/disable x-sendfile
|
||||
``LOGGER_NAME`` the name of the logger
|
||||
``LOGGER_HANDLER_POLICY`` the policy of the default logging
|
||||
handler. The default is ``'always'``
|
||||
which means that the default logging
|
||||
handler is always active. ``'debug'``
|
||||
will only activate logging in debug
|
||||
mode, ``'production'`` will only log in
|
||||
production and ``'never'`` disables it
|
||||
entirely.
|
||||
``SERVER_NAME`` the name and port number of the server.
|
||||
Required for subdomain support (e.g.:
|
||||
``'myapp.dev:5000'``) Note that
|
||||
|
|
@ -108,22 +132,23 @@ 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
|
||||
returning a 413 status code.
|
||||
``SEND_FILE_MAX_AGE_DEFAULT``: Default cache control max age to use with
|
||||
``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
|
||||
|
|
@ -168,18 +193,23 @@ 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)
|
||||
``TEMPLATES_AUTO_RELOAD`` Flask checks if template was modified each
|
||||
time it is requested and reloads it if
|
||||
necessary. But disk I/O is costly and it may
|
||||
be viable to disable this feature by setting
|
||||
this key to ``False``. This option does not
|
||||
affect debug mode.
|
||||
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
|
||||
``None`` which means that Flask checks
|
||||
original file only in debug mode.
|
||||
``EXPLAIN_TEMPLATE_LOADING`` If this is enabled then every attempt to
|
||||
load a template will write an info
|
||||
message to the logger explaining the
|
||||
attempts to locate the template. This
|
||||
can be useful to figure out why
|
||||
templates cannot be found or wrong
|
||||
templates appear to be loaded.
|
||||
================================= =========================================
|
||||
|
||||
.. admonition:: More on ``SERVER_NAME``
|
||||
|
|
@ -194,12 +224,12 @@ The following configuration values are used internally by Flask:
|
|||
browsers will not allow cross-subdomain cookies to be set on a
|
||||
server name without dots in it. So if your server name is
|
||||
``'localhost'`` you will not be able to set a cookie for
|
||||
``'localhost'`` and every subdomain of it. Please chose a different
|
||||
``'localhost'`` and every subdomain of it. Please choose a different
|
||||
server name in that case, like ``'myapplication.local'`` and add
|
||||
this name + the subdomains you want to use into your host config
|
||||
or setup a local `bind`_.
|
||||
|
||||
.. _bind: https://www.isc.org/software/bind
|
||||
.. _bind: https://www.isc.org/downloads/bind/
|
||||
|
||||
.. versionadded:: 0.4
|
||||
``LOGGER_NAME``
|
||||
|
|
@ -225,11 +255,9 @@ The following configuration values are used internally by Flask:
|
|||
.. versionadded:: 0.10
|
||||
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_PRETTYPRINT_REGULAR``
|
||||
|
||||
.. versionadded:: 1.0
|
||||
``SESSION_REFRESH_EACH_REQUEST``
|
||||
|
||||
.. versionadded:: 1.0
|
||||
``TEMPLATES_AUTO_RELOAD``
|
||||
.. versionadded:: 0.11
|
||||
``SESSION_REFRESH_EACH_REQUEST``, ``TEMPLATES_AUTO_RELOAD``,
|
||||
``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING``
|
||||
|
||||
Configuring from Files
|
||||
----------------------
|
||||
|
|
@ -287,7 +315,7 @@ a little harder. There is no single 100% solution for this problem in
|
|||
general, but there are a couple of things you can keep in mind to improve
|
||||
that experience:
|
||||
|
||||
1. create your application in a function and register blueprints on it.
|
||||
1. Create your application in a function and register blueprints on it.
|
||||
That way you can create multiple instances of your application with
|
||||
different configurations attached which makes unittesting a lot
|
||||
easier. You can use this to pass in configuration as needed.
|
||||
|
|
@ -296,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
|
||||
------------------------
|
||||
|
|
@ -311,13 +340,13 @@ in the example above::
|
|||
app.config.from_object('yourapplication.default_settings')
|
||||
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
|
||||
|
||||
Then you just have to add a separate `config.py` file and export
|
||||
Then you just have to add a separate :file:`config.py` file and export
|
||||
``YOURAPPLICATION_SETTINGS=/path/to/config.py`` and you are done. However
|
||||
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
|
||||
|
|
@ -348,10 +377,10 @@ To enable such a config you just have to call into
|
|||
There are many different ways and it's up to you how you want to manage
|
||||
your configuration files. However here a list of good recommendations:
|
||||
|
||||
- keep a default configuration in version control. Either populate the
|
||||
- Keep a default configuration in version control. Either populate the
|
||||
config with this default configuration or import it in your own
|
||||
configuration files before overriding values.
|
||||
- use an environment variable to switch between the configurations.
|
||||
- Use an environment variable to switch between the configurations.
|
||||
This can be done from outside the Python interpreter and makes
|
||||
development and deployment much easier because you can quickly and
|
||||
easily switch between different configs without having to touch the
|
||||
|
|
@ -363,7 +392,7 @@ your configuration files. However here a list of good recommendations:
|
|||
details about how to do that, head over to the
|
||||
:ref:`fabric-deployment` pattern.
|
||||
|
||||
.. _fabric: http://fabfile.org/
|
||||
.. _fabric: http://www.fabfile.org/
|
||||
|
||||
|
||||
.. _instance-folders:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ instructions for web development with Flask.
|
|||
reqcontext
|
||||
blueprints
|
||||
extensions
|
||||
cli
|
||||
server
|
||||
shell
|
||||
patterns/index
|
||||
deploying/index
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Creating a `.cgi` file
|
|||
----------------------
|
||||
|
||||
First you need to create the CGI application file. Let's call it
|
||||
`yourapplication.cgi`::
|
||||
:file:`yourapplication.cgi`::
|
||||
|
||||
#!/usr/bin/python
|
||||
from wsgiref.handlers import CGIHandler
|
||||
|
|
@ -36,7 +36,7 @@ Server Setup
|
|||
------------
|
||||
|
||||
Usually there are two ways to configure the server. Either just copy the
|
||||
`.cgi` into a `cgi-bin` (and use `mod_rewrite` or something similar to
|
||||
``.cgi`` into a :file:`cgi-bin` (and use `mod_rewrite` or something similar to
|
||||
rewrite the URL) or let the server point to the file directly.
|
||||
|
||||
In Apache for example you can put something like this into the config:
|
||||
|
|
@ -46,16 +46,16 @@ In Apache for example you can put something like this into the config:
|
|||
ScriptAlias /app /path/to/the/application.cgi
|
||||
|
||||
On shared webhosting, though, you might not have access to your Apache config.
|
||||
In this case, a file called `.htaccess`, sitting in the public directory you want
|
||||
your app to be available, works too but the `ScriptAlias` directive won't
|
||||
In this case, a file called ``.htaccess``, sitting in the public directory you want
|
||||
your app to be available, works too but the ``ScriptAlias`` directive won't
|
||||
work in that case:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
|
||||
RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]
|
||||
|
||||
For more information consult the documentation of your webserver.
|
||||
|
||||
.. _App Engine: http://code.google.com/appengine/
|
||||
.. _App Engine: https://developers.google.com/appengine/
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ socket to the :class:`~flup.server.fcgi.WSGIServer`::
|
|||
The path has to be the exact same path you define in the server
|
||||
config.
|
||||
|
||||
Save the `yourapplication.fcgi` file somewhere you will find it again.
|
||||
It makes sense to have that in `/var/www/yourapplication` or something
|
||||
Save the :file:`yourapplication.fcgi` file somewhere you will find it again.
|
||||
It makes sense to have that in :file:`/var/www/yourapplication` or something
|
||||
similar.
|
||||
|
||||
Make sure to set the executable bit on that file so that the servers
|
||||
|
|
@ -56,7 +56,7 @@ Configuring Apache
|
|||
|
||||
The example above is good enough for a basic Apache deployment but your
|
||||
`.fcgi` file will appear in your application URL e.g.
|
||||
example.com/yourapplication.fcgi/news/. There are few ways to configure
|
||||
``example.com/yourapplication.fcgi/news/``. There are few ways to configure
|
||||
your application so that yourapplication.fcgi does not appear in the URL.
|
||||
A preferable way is to use the ScriptAlias and SetHandler configuration
|
||||
directives to route requests to the FastCGI server. The following example
|
||||
|
|
@ -79,7 +79,7 @@ handle all incoming requests::
|
|||
</Location>
|
||||
</VirtualHost>
|
||||
|
||||
These processes will be managed by Apache. If you're using an standalone
|
||||
These processes will be managed by Apache. If you're using a standalone
|
||||
FastCGI server, you can use the FastCgiExternalServer directive instead.
|
||||
Note that in the following the path is not real, it's simply used as an
|
||||
identifier to other
|
||||
|
|
@ -87,7 +87,7 @@ directives such as AliasMatch::
|
|||
|
||||
FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000
|
||||
|
||||
If you cannot set ScriptAlias, for example on an shared web host, you can use
|
||||
If you cannot set ScriptAlias, for example on a shared web host, you can use
|
||||
WSGI middleware to remove yourapplication.fcgi from the URLs. Set .htaccess::
|
||||
|
||||
<IfModule mod_fcgid.c>
|
||||
|
|
@ -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 = (
|
||||
|
|
@ -153,13 +153,13 @@ A basic FastCGI configuration for lighttpd looks like that::
|
|||
)
|
||||
|
||||
Remember to enable the FastCGI, alias and rewrite modules. This configuration
|
||||
binds the application to `/yourapplication`. If you want the application to
|
||||
binds the application to ``/yourapplication``. If you want the application to
|
||||
work in the URL root you have to work around a lighttpd bug with the
|
||||
:class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
|
||||
|
||||
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/wiki/lighttpd/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
|
||||
|
|
@ -168,7 +168,7 @@ Configuring nginx
|
|||
Installing FastCGI applications on nginx is a bit different because by
|
||||
default no FastCGI parameters are forwarded.
|
||||
|
||||
A basic flask FastCGI configuration for nginx looks like this::
|
||||
A basic Flask FastCGI configuration for nginx looks like this::
|
||||
|
||||
location = /yourapplication { rewrite ^ /yourapplication/ last; }
|
||||
location /yourapplication { try_files $uri @yourapplication; }
|
||||
|
|
@ -180,9 +180,9 @@ A basic flask FastCGI configuration for nginx looks like this::
|
|||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
||||
}
|
||||
|
||||
This configuration binds the application to `/yourapplication`. If you
|
||||
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 figure out how to calculate `PATH_INFO` and `SCRIPT_NAME`::
|
||||
have to figure out how to calculate ``PATH_INFO`` and ``SCRIPT_NAME``::
|
||||
|
||||
location / { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
|
|
@ -195,7 +195,7 @@ have to figure out how to calculate `PATH_INFO` and `SCRIPT_NAME`::
|
|||
Running FastCGI Processes
|
||||
-------------------------
|
||||
|
||||
Since Nginx and others do not load FastCGI apps, you have to do it by
|
||||
Since nginx and others do not load FastCGI apps, you have to do it by
|
||||
yourself. `Supervisor can manage FastCGI processes.
|
||||
<http://supervisord.org/configuration.html#fcgi-program-x-section-settings>`_
|
||||
You can look around for other FastCGI process managers or write a script
|
||||
|
|
@ -210,14 +210,14 @@ manual solution which does not persist across system restart::
|
|||
Debugging
|
||||
---------
|
||||
|
||||
FastCGI deployments tend to be hard to debug on most webservers. Very
|
||||
FastCGI deployments tend to be hard to debug on most web servers. Very
|
||||
often the only thing the server log tells you is something along the
|
||||
lines of "premature end of headers". In order to debug the application
|
||||
the only thing that can really give you ideas why it breaks is switching
|
||||
to the correct user and executing the application by hand.
|
||||
|
||||
This example assumes your application is called `application.fcgi` and
|
||||
that your webserver user is `www-data`::
|
||||
that your web server user is `www-data`::
|
||||
|
||||
$ su www-data
|
||||
$ cd /var/www/yourapplication
|
||||
|
|
@ -229,12 +229,12 @@ that your webserver user is `www-data`::
|
|||
In this case the error seems to be "yourapplication" not being on the
|
||||
python path. Common problems are:
|
||||
|
||||
- Relative paths being used. Don't rely on the current working directory
|
||||
- Relative paths being used. Don't rely on the current working directory.
|
||||
- The code depending on environment variables that are not set by the
|
||||
web server.
|
||||
- Different python interpreters being used.
|
||||
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _cherokee: http://www.cherokee-project.com/
|
||||
.. _flup: http://trac.saddi.com/flup
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _flup: https://pypi.python.org/pypi/flup
|
||||
|
|
|
|||
|
|
@ -3,18 +3,31 @@
|
|||
Deployment Options
|
||||
==================
|
||||
|
||||
Depending on what you have available there are multiple ways to run
|
||||
Flask applications. You can use the builtin server during development,
|
||||
but you should use a full deployment option for production applications.
|
||||
(Do not use the builtin development server in production.) Several
|
||||
options are available and documented here.
|
||||
While lightweight and easy to use, **Flask's built-in server is not suitable
|
||||
for production** as it doesn't scale well and by default serves only one
|
||||
request at a time. Some of the options available for properly running Flask in
|
||||
production are documented here.
|
||||
|
||||
If you have a different WSGI server look up the server documentation
|
||||
about how to use a WSGI app with it. Just remember that your
|
||||
:class:`Flask` application object is the actual WSGI application.
|
||||
If you want to deploy your Flask application to a WSGI server not listed here,
|
||||
look up the server documentation about how to use a WSGI app with it. Just
|
||||
remember that your :class:`Flask` application object is the actual WSGI
|
||||
application.
|
||||
|
||||
For hosted options to get up and running quickly, see
|
||||
:ref:`quickstart_deployment` in the Quickstart.
|
||||
|
||||
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 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
|
||||
-------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
---------------------
|
||||
|
|
@ -29,12 +29,19 @@ follows:
|
|||
|
||||
# apt-get install libapache2-mod-wsgi
|
||||
|
||||
If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you
|
||||
can install it as follows:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
# yum install mod_wsgi
|
||||
|
||||
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
|
||||
using pkg_add:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
# pkg_add -r mod_wsgi
|
||||
# pkg install ap22-mod_wsgi2
|
||||
|
||||
If you are using pkgsrc you can install `mod_wsgi` by compiling the
|
||||
`www/ap2-wsgi` package.
|
||||
|
|
@ -45,7 +52,7 @@ reload you can safely ignore them. Just restart the server.
|
|||
Creating a `.wsgi` file
|
||||
-----------------------
|
||||
|
||||
To run your application you need a `yourapplication.wsgi` file. This file
|
||||
To run your application you need a :file:`yourapplication.wsgi` file. This file
|
||||
contains the code `mod_wsgi` is executing on startup to get the application
|
||||
object. The object called `application` in that file is then used as
|
||||
application.
|
||||
|
|
@ -58,12 +65,12 @@ If you don't have a factory function for application creation but a singleton
|
|||
instance you can directly import that one as `application`.
|
||||
|
||||
Store that file somewhere that you will find it again (e.g.:
|
||||
`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
||||
:file:`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
||||
the libraries that are in use are on the python load path. If you don't
|
||||
want to install it system wide consider using a `virtual python`_
|
||||
instance. Keep in mind that you will have to actually install your
|
||||
application into the virtualenv as well. Alternatively there is the
|
||||
option to just patch the path in the `.wsgi` file before the import::
|
||||
option to just patch the path in the ``.wsgi`` file before the import::
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '/path/to/the/application')
|
||||
|
|
@ -91,7 +98,7 @@ execute the application under a different user for security reasons:
|
|||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
|
||||
Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
|
||||
refuse to run with the above configuration. On a Windows system, eliminate those lines:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
|
@ -105,12 +112,30 @@ refuse to run with the above configuration. On a Windows system, eliminate those
|
|||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
For more information consult the `mod_wsgi wiki`_.
|
||||
Note: There have been some changes in access control configuration for `Apache 2.4`_.
|
||||
|
||||
.. _mod_wsgi: http://code.google.com/p/modwsgi/
|
||||
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
|
||||
.. _virtual python: http://pypi.python.org/pypi/virtualenv
|
||||
.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/
|
||||
.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
|
||||
|
||||
Most notably, the syntax for directory permissions has changed from httpd 2.2
|
||||
|
||||
.. 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 documentation: https://modwsgi.readthedocs.io/en/develop/index.html
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
|
@ -118,10 +143,10 @@ 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 `run.py` file or put it into such an if block.
|
||||
into a separate :file:`run.py` file or put it into such an if block.
|
||||
|
||||
**Problem:** application gives permission errors
|
||||
Probably caused by your application running as the wrong user. Make
|
||||
|
|
@ -160,7 +185,7 @@ Support for Automatic Reloading
|
|||
-------------------------------
|
||||
|
||||
To help deployment tools you can activate support for automatic
|
||||
reloading. Whenever something changes the `.wsgi` file, `mod_wsgi` will
|
||||
reloading. Whenever something changes the ``.wsgi`` file, `mod_wsgi` will
|
||||
reload all the daemon processes for us.
|
||||
|
||||
For that, just add the following directive to your `Directory` section:
|
||||
|
|
@ -175,12 +200,18 @@ Working with Virtual Environments
|
|||
Virtual environments have the advantage that they never install the
|
||||
required dependencies system wide so you have a better control over what
|
||||
is used where. If you want to use a virtual environment with mod_wsgi
|
||||
you have to modify your `.wsgi` file slightly.
|
||||
you have to modify your ``.wsgi`` file slightly.
|
||||
|
||||
Add the following lines to the top of your `.wsgi` file::
|
||||
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::
|
||||
This configuration binds the application to ``/yourapplication``. If you want
|
||||
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/
|
||||
.. _cherokee: http://www.cherokee-project.com/
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _uwsgi: http://projects.unbit.it/uwsgi/
|
||||
|
|
|
|||
|
|
@ -25,35 +25,13 @@ 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/
|
||||
|
||||
Tornado
|
||||
--------
|
||||
|
||||
`Tornado`_ is an open source version of the scalable, non-blocking web
|
||||
server and tools that power `FriendFeed`_. Because it is non-blocking and
|
||||
uses epoll, it can handle thousands of simultaneous standing connections,
|
||||
which means it is ideal for real-time web services. Integrating this
|
||||
service with Flask is straightforward::
|
||||
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
from yourapplication import app
|
||||
|
||||
http_server = HTTPServer(WSGIContainer(app))
|
||||
http_server.listen(5000)
|
||||
IOLoop.instance().start()
|
||||
|
||||
|
||||
.. _Tornado: http://www.tornadoweb.org/
|
||||
.. _FriendFeed: http://friendfeed.com/
|
||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||
|
||||
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
|
||||
|
|
@ -63,8 +41,8 @@ event loop::
|
|||
http_server.serve_forever()
|
||||
|
||||
.. _Gevent: http://www.gevent.org/
|
||||
.. _greenlet: http://greenlet.readthedocs.org/en/latest/
|
||||
.. _libevent: http://monkey.org/~provos/libevent/
|
||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||
.. _libev: http://software.schmorp.de/pkg/libev.html
|
||||
|
||||
Twisted Web
|
||||
-----------
|
||||
|
|
@ -96,8 +74,8 @@ Proxy Setups
|
|||
|
||||
If you deploy your application using one of these servers behind an HTTP proxy
|
||||
you will need to rewrite a few headers in order for the application to work.
|
||||
The two problematic values in the WSGI environment usually are `REMOTE_ADDR`
|
||||
and `HTTP_HOST`. You can configure your httpd to pass these headers, or you
|
||||
The two problematic values in the WSGI environment usually are ``REMOTE_ADDR``
|
||||
and ``HTTP_HOST``. You can configure your httpd to pass these headers, or you
|
||||
can fix them in middleware. Werkzeug ships a fixer that will solve some common
|
||||
setups, but you might want to write your own WSGI middleware for specific
|
||||
setups.
|
||||
|
|
@ -119,15 +97,16 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
If your httpd is not providing these headers, the most common setup invokes the
|
||||
host being set from `X-Forwarded-Host` and the remote address from
|
||||
`X-Forwarded-For`::
|
||||
host being set from ``X-Forwarded-Host`` and the remote address from
|
||||
``X-Forwarded-For``::
|
||||
|
||||
from werkzeug.contrib.fixers import ProxyFix
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
.. _application-errors:
|
||||
|
||||
Logging Application Errors
|
||||
==========================
|
||||
Application Errors
|
||||
==================
|
||||
|
||||
.. versionadded:: 0.3
|
||||
|
||||
Applications fail, servers fail. Sooner or later you will see an exception
|
||||
in production. Even if your code is 100% correct, you will still see
|
||||
exceptions from time to time. Why? Because everything else involved will
|
||||
fail. Here some situations where perfectly fine code can lead to server
|
||||
fail. Here are some situations where perfectly fine code can lead to server
|
||||
errors:
|
||||
|
||||
- the client terminated the request early and the application was still
|
||||
reading from the incoming data.
|
||||
- the database server was overloaded and could not handle the query.
|
||||
reading from the incoming data
|
||||
- the database server was overloaded and could not handle the query
|
||||
- a filesystem is full
|
||||
- a harddrive crashed
|
||||
- a backend server overloaded
|
||||
- a programming error in a library you are using
|
||||
- network connection of the server to another system failed.
|
||||
- network connection of the server to another system failed
|
||||
|
||||
And that's just a small sample of issues you could be facing. So how do we
|
||||
deal with that sort of problem? By default if your application runs in
|
||||
|
|
@ -28,22 +28,112 @@ 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
|
||||
--------------
|
||||
|
||||
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 raised while trying to
|
||||
do something else.
|
||||
|
||||
Registering
|
||||
```````````
|
||||
|
||||
Register error handlers using :meth:`~flask.Flask.errorhandler` or
|
||||
:meth:`~flask.Flask.register_error_handler`::
|
||||
|
||||
@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 interchangeable when handed to the registration methods or decorator
|
||||
(``BadRequest.code == 400``).
|
||||
|
||||
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:: 0.11
|
||||
|
||||
Errorhandlers are now prioritized by specificity of the exception classes
|
||||
they are registered for instead of the order they are registered in.
|
||||
|
||||
Handling
|
||||
````````
|
||||
|
||||
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 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.
|
||||
|
||||
Error Mails
|
||||
-----------
|
||||
|
||||
If the application runs in production mode (which it will do on your
|
||||
server) you won't see any log messages by default. Why is that? Flask
|
||||
tries to be a zero-configuration framework. Where should it drop the logs
|
||||
for you if there is no configuration? Guessing is not a good idea because
|
||||
chances are, the place it guessed is not the place where the user has
|
||||
permission to create a logfile. Also, for most small applications nobody
|
||||
will look at the logs anyways.
|
||||
server) you might not see any log messages. The reason for that is that
|
||||
Flask by default will just report to the WSGI error stream or stderr
|
||||
(depending on what's available). Where this ends up is sometimes hard to
|
||||
find. Often it's in your webserver's log files.
|
||||
|
||||
In fact, I promise you right now that if you configure a logfile for the
|
||||
application errors you will never look at it except for debugging an issue
|
||||
when a user reported it for you. What you want instead is a mail the
|
||||
second the exception happened. Then you get an alert and you can do
|
||||
something about it.
|
||||
I can pretty much promise you however that if you only use a logfile for
|
||||
the application errors you will never look at it except for debugging an
|
||||
issue when a user reported it for you. What you probably want instead is
|
||||
a mail the second the exception happened. Then you get an alert and you
|
||||
can do something about it.
|
||||
|
||||
Flask uses the Python builtin logging system, and it can actually send
|
||||
you mails for errors which is probably what you want. Here is how you can
|
||||
|
|
@ -81,9 +171,10 @@ 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. 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.
|
||||
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.
|
||||
|
||||
There are a couple of handlers provided by the logging system out of the
|
||||
box but not all of them are useful for basic error logging. The most
|
||||
|
|
@ -125,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
|
||||
`````
|
||||
|
|
@ -185,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 |
|
||||
|
|
|
|||
|
|
@ -27,22 +27,16 @@ The name of the actual extension (the human readable name) however would
|
|||
be something like "Flask-SimpleXML". Make sure to include the name
|
||||
"Flask" somewhere in that name and that you check the capitalization.
|
||||
This is how users can then register dependencies to your extension in
|
||||
their `setup.py` files.
|
||||
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
|
||||
unittests and to support multiple configurations. Because of that it is
|
||||
crucial that your application supports that kind of behavior.
|
||||
|
||||
Most importantly the extension must be shipped with a `setup.py` file and
|
||||
Most importantly the extension must be shipped with a :file:`setup.py` file and
|
||||
registered on PyPI. Also the development checkout link should work so
|
||||
that people can easily install the development version into their
|
||||
virtualenv without having to download the library by hand.
|
||||
|
|
@ -70,7 +64,7 @@ Here's the contents of the most important files:
|
|||
setup.py
|
||||
````````
|
||||
|
||||
The next file that is absolutely required is the `setup.py` file which is
|
||||
The next file that is absolutely required is the :file:`setup.py` file which is
|
||||
used to install your Flask extension. The following contents are
|
||||
something you can work with::
|
||||
|
||||
|
|
@ -259,7 +253,7 @@ way::
|
|||
cur = db.connection.cursor()
|
||||
cur.execute(...)
|
||||
|
||||
At the end of the `with` block the teardown handles will be executed
|
||||
At the end of the ``with`` block the teardown handles will be executed
|
||||
automatically.
|
||||
|
||||
Additionally, the ``init_app`` method is used to support the factory pattern
|
||||
|
|
@ -360,13 +354,12 @@ extension to be approved you have to follow these guidelines:
|
|||
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``. They might also reside inside a
|
||||
``flaskext`` namespace packages though this is discouraged now.
|
||||
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``, 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:
|
||||
|
|
@ -380,7 +373,7 @@ extension to be approved you have to follow these guidelines:
|
|||
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
|
||||
: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.
|
||||
|
|
@ -394,27 +387,24 @@ extension to be approved you have to follow these guidelines:
|
|||
Python 2.7
|
||||
|
||||
|
||||
.. _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.ext.foo``
|
||||
instead of ``flask_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://packages.python.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
|
||||
================
|
||||
|
||||
|
|
@ -8,8 +10,8 @@ Finding Extensions
|
|||
------------------
|
||||
|
||||
Flask extensions are listed on the `Flask Extension Registry`_ and can be
|
||||
downloaded with ``easy_install`` or ``pip``. If you add a Flask extension
|
||||
as dependency to your ``requirements.rst`` or ``setup.py`` file they are
|
||||
downloaded with :command:`easy_install` or :command:`pip`. If you add a Flask extension
|
||||
as dependency to your :file:`requirements.txt` or :file:`setup.py` file they are
|
||||
usually installed with a simple command or when your application installs.
|
||||
|
||||
Using 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
|
||||
----------------
|
||||
|
|
@ -32,7 +41,7 @@ depending on how the extension is distributed. If you want to develop an
|
|||
application that supports Flask 0.7 or earlier you should still import
|
||||
from the :data:`flask.ext` package. We provide you with a compatibility
|
||||
module that provides this package for older versions of Flask. You can
|
||||
download it from github: `flaskext_compat.py`_
|
||||
download it from GitHub: `flaskext_compat.py`_
|
||||
|
||||
And here is how you can use it::
|
||||
|
||||
|
|
@ -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://github.com/mitsuhiko/flask/raw/master/scripts/flaskext_compat.py
|
||||
.. _flaskext_compat.py: https://raw.githubusercontent.com/pallets/flask/master/scripts/flaskext_compat.py
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ What does "micro" mean?
|
|||
-----------------------
|
||||
|
||||
“Micro” does not mean that your whole web application has to fit into a single
|
||||
Python file, although it certainly can. Nor does it mean that Flask is lacking
|
||||
Python file (although it certainly can), nor does it mean that Flask is lacking
|
||||
in functionality. The "micro" in microframework means Flask aims to keep the
|
||||
core simple but extensible. Flask won't make many decisions for you, such as
|
||||
what database to use. Those decisions that it does make, such as what
|
||||
|
|
@ -28,9 +28,9 @@ Configuration and Conventions
|
|||
-----------------------------
|
||||
|
||||
Flask has many configuration values, with sensible defaults, and a few
|
||||
conventions when getting started. By convention templates and static files are
|
||||
conventions when getting started. By convention, templates and static files are
|
||||
stored in subdirectories within the application's Python source tree, with the
|
||||
names `templates` and `static` respectively. While this can be changed you
|
||||
names :file:`templates` and :file:`static` respectively. While this can be changed, you
|
||||
usually don't have to, especially when getting started.
|
||||
|
||||
Growing with Flask
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ However, barely any websites on the Internet are actual XHTML (which is
|
|||
HTML processed using XML rules). There are a couple of major reasons
|
||||
why this is the case. One of them is Internet Explorer's lack of proper
|
||||
XHTML support. The XHTML spec states that XHTML must be served with the MIME
|
||||
type `application/xhtml+xml`, but Internet Explorer refuses to read files
|
||||
type :mimetype:`application/xhtml+xml`, but Internet Explorer refuses to read files
|
||||
with that MIME type.
|
||||
While it is relatively easy to configure Web servers to serve XHTML properly,
|
||||
few people do. This is likely because properly using XHTML can be quite
|
||||
|
|
@ -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.
|
||||
|
|
@ -186,7 +186,7 @@ Many other features have been added, as well. A good guide to new features
|
|||
in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_.
|
||||
Not all of them are supported in browsers yet, however, so use caution.
|
||||
|
||||
.. _Dive Into HTML5: http://www.diveintohtml5.org/
|
||||
.. _Dive Into HTML5: http://diveintohtml5.info/
|
||||
|
||||
What should be used?
|
||||
--------------------
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented
|
|||
here. If you want to dive into their documentation, check out the
|
||||
following links:
|
||||
|
||||
- `Jinja2 Documentation <http://jinja.pocoo.org/2/documentation/>`_
|
||||
- `Werkzeug Documentation <http://werkzeug.pocoo.org/documentation/>`_
|
||||
- `Jinja2 Documentation <http://jinja.pocoo.org/docs>`_
|
||||
- `Werkzeug Documentation <http://werkzeug.pocoo.org/docs>`_
|
||||
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/2/
|
||||
.. _Jinja2: http://jinja.pocoo.org/
|
||||
.. _Werkzeug: http://werkzeug.pocoo.org/
|
||||
|
||||
.. include:: contents.rst.inc
|
||||
|
|
|
|||
|
|
@ -3,179 +3,173 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
Flask depends on some external libraries, like `Werkzeug
|
||||
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/2/>`_.
|
||||
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 `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 `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 `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:
|
||||
|
||||
Now you can just enter the following command to get Flask activated in your
|
||||
virtualenv::
|
||||
.. code-block:: bat
|
||||
|
||||
$ pip install Flask
|
||||
py -3 -m venv venv
|
||||
|
||||
A few seconds later and you are good to go.
|
||||
If you needed to install virtualenv because you are on an older version of
|
||||
Python, use the following command instead:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
System-Wide Installation
|
||||
------------------------
|
||||
virtualenv venv
|
||||
|
||||
This is possible as well, though I do not recommend it. Just run
|
||||
`pip` with root privileges::
|
||||
On Windows:
|
||||
|
||||
$ sudo pip install Flask
|
||||
.. code-block:: bat
|
||||
|
||||
(On Windows systems, run it in a command-prompt window with administrator
|
||||
privileges, and leave out `sudo`.)
|
||||
\Python27\Scripts\virtualenv.exe venv
|
||||
|
||||
Activate the environment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Living on the Edge
|
||||
Before you work on your project, activate the corresponding environment:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
. venv/bin/activate
|
||||
|
||||
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 `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
|
||||
|
||||
To just get the development version without git, do this instead::
|
||||
# CentOS, Fedora
|
||||
sudo yum install python-virtualenv
|
||||
|
||||
$ mkdir flask
|
||||
$ cd flask
|
||||
$ virtualenv venv
|
||||
$ . venv/bin/activate
|
||||
New python executable in venv/bin/python
|
||||
Installing setuptools, pip............done.
|
||||
$ pip install Flask==dev
|
||||
...
|
||||
Finished processing dependencies for Flask==dev
|
||||
# Arch
|
||||
sudo pacman -S python-virtualenv
|
||||
|
||||
.. _windows-easy-install:
|
||||
If you are on Mac OS X or Windows, download `get-pip.py`_, then:
|
||||
|
||||
`pip` and `setuptools` on Windows
|
||||
---------------------------------
|
||||
.. code-block:: sh
|
||||
|
||||
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.
|
||||
sudo python2 Downloads/get-pip.py
|
||||
sudo python2 -m pip install virtualenv
|
||||
|
||||
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).
|
||||
On Windows, as an administrator:
|
||||
|
||||
`get-pip.py`_
|
||||
.. code-block:: bat
|
||||
|
||||
To install the latest setuptools, you can use its bootstrap file:
|
||||
\Python27\python.exe Downloads\get-pip.py
|
||||
\Python27\python.exe -m pip install virtualenv
|
||||
|
||||
`ez_setup.py`_
|
||||
Now you can continue to :ref:`install-create-env`.
|
||||
|
||||
Either should be double-clickable once you download them. If you already have pip,
|
||||
you can upgrade them by running::
|
||||
|
||||
> pip install --upgrade pip setuptools
|
||||
|
||||
Most often, once you pull up a command prompt you want to be able to type ``pip``
|
||||
and ``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 ``C:\Python27``), then go to ``Tools``, then ``Scripts``; then find the
|
||||
``win_add2path.py`` file and run that. Open a **new** Command Prompt and
|
||||
check that you can now just type ``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.github.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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ documentation.
|
|||
- "AUTHORS" hereby refers to all the authors listed in the
|
||||
:ref:`authors` section.
|
||||
|
||||
- The ":ref:`flask-license`" applies to all the sourcecode shipped as
|
||||
- The ":ref:`flask-license`" applies to all the source code shipped as
|
||||
part of Flask (Flask itself as well as the examples and the unittests)
|
||||
as well as documentation.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ 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
|
||||
``text/html`` they are emitting is not very useful for API consumers.
|
||||
:mimetype:`text/html` they are emitting is not very useful for API consumers.
|
||||
|
||||
The better solution than using ``abort`` to signal errors for invalid API
|
||||
usage is to implement your own exception type and install an error handler
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -30,8 +30,8 @@ at :func:`werkzeug.serving.run_simple`::
|
|||
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 debuggger, debugging must be enabled both on
|
||||
the application and the simple server, here is the "hello world" example with
|
||||
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
|
||||
debugging and :func:`run_simple <werkzeug.serving.run_simple>`::
|
||||
|
||||
from flask import Flask
|
||||
|
|
@ -56,11 +56,11 @@ 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
|
||||
backend interface on `/backend`::
|
||||
For example you could have your main application run on ``/`` and your
|
||||
backend interface on ``/backend``::
|
||||
|
||||
from werkzeug.wsgi import DispatcherMiddleware
|
||||
from frontend_app import application as frontend
|
||||
|
|
@ -144,7 +144,7 @@ Dispatch by Path
|
|||
----------------
|
||||
|
||||
Dispatching by a path on the URL is very similar. Instead of looking at
|
||||
the `Host` header to figure out the subdomain one simply looks at the
|
||||
the ``Host`` header to figure out the subdomain one simply looks at the
|
||||
request path up to the first slash::
|
||||
|
||||
from threading import Lock
|
||||
|
|
@ -176,7 +176,7 @@ request path up to the first slash::
|
|||
return app(environ, start_response)
|
||||
|
||||
The big difference between this and the subdomain one is that this one
|
||||
falls back to another application if the creator function returns `None`::
|
||||
falls back to another application if the creator function returns ``None``::
|
||||
|
||||
from myapplication import create_app, default_app, get_user_for_prefix
|
||||
|
||||
|
|
|
|||
|
|
@ -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,9 +60,9 @@ 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):
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile(config_filename)
|
||||
|
|
@ -72,7 +72,7 @@ as an example, you should not do something along those lines::
|
|||
But, rather, in model.py (or equivalent)::
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
|
||||
and in your application.py (or equivalent)::
|
||||
|
||||
def create_app(config_filename):
|
||||
|
|
@ -83,18 +83,24 @@ and in your application.py (or equivalent)::
|
|||
db.init_app(app)
|
||||
|
||||
Using this design pattern, no application-specific state is stored on the
|
||||
extension object, so one extension object can be used for multiple apps.
|
||||
extension object, so one extension object can be used for multiple apps.
|
||||
For more information about the design of extensions refer to :doc:`/extensiondev`.
|
||||
|
||||
Using Applications
|
||||
------------------
|
||||
|
||||
So to use such an application you then have to create the application
|
||||
first. Here an example `run.py` file that runs such an application::
|
||||
first in a separate file otherwise the :command:`flask` command won't be able
|
||||
to find it. Here an example :file:`exampleapp.py` file that creates such
|
||||
an application::
|
||||
|
||||
from yourapplication import create_app
|
||||
app = create_app('/path/to/config.cfg')
|
||||
app.run()
|
||||
|
||||
It can then be used with the :command:`flask` command::
|
||||
|
||||
export FLASK_APP=exampleapp
|
||||
flask run
|
||||
|
||||
Factory Improvements
|
||||
--------------------
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ cache that keeps the item stored in the memory of the Python interpreter::
|
|||
cache = SimpleCache()
|
||||
|
||||
If you want to use memcached, make sure to have one of the memcache modules
|
||||
supported (you get them from `PyPI <http://pypi.python.org/>`_) and a
|
||||
supported (you get them from `PyPI <https://pypi.python.org/pypi>`_) and a
|
||||
memcached server running somewhere. This is how you connect to such an
|
||||
memcached server then::
|
||||
|
||||
|
|
@ -44,13 +44,13 @@ Using a Cache
|
|||
-------------
|
||||
|
||||
Now how can one use such a cache? There are two very important
|
||||
operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and
|
||||
operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and
|
||||
:meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them:
|
||||
|
||||
To get an item from the cache call
|
||||
:meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name.
|
||||
If something is in the cache, it is returned. Otherwise that function
|
||||
will return `None`::
|
||||
will return ``None``::
|
||||
|
||||
rv = cache.get('my-item')
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ``pip`` or ``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,14 +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, 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
|
||||
|
||||
|
|
@ -52,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
|
||||
|
||||
|
|
@ -67,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,47 +15,41 @@ 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**: `easy_install`, which comes with distribute
|
||||
can install other libraries for you. You can also use `pip`_ which
|
||||
sooner or later will replace `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
|
||||
`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.
|
||||
:file:`yourapplication.py` and you are not using a module, but a :ref:`package
|
||||
<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>`.
|
||||
|
||||
Your setup code always goes into a file named `setup.py` next to your
|
||||
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 `setup.py` file for a Flask application looks like this::
|
||||
A basic :file:`setup.py` file for a Flask application looks like this::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
|
@ -71,8 +64,8 @@ A basic `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,41 +74,60 @@ 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 `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
|
||||
----------------------
|
||||
|
||||
If you try to install the package you just created, you will notice that
|
||||
folders like `static` or `templates` are not installed for you. The
|
||||
reason for this is that distribute does not know which files to add for
|
||||
you. What you should do, is to create a `MANIFEST.in` file next to your
|
||||
`setup.py` file. This file lists all the files that should be added to
|
||||
folders like :file:`static` or :file:`templates` are not installed for you. The
|
||||
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::
|
||||
|
||||
recursive-include yourapplication/templates *
|
||||
recursive-include yourapplication/static *
|
||||
|
||||
Don't forget that even if you enlist them in your `MANIFEST.in` file, they
|
||||
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
|
||||
`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: http://pypi.python.org/pypi/distribute
|
||||
.. _pip: http://pypi.python.org/pypi/pip
|
||||
.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py
|
||||
.. _pip: https://pypi.python.org/pypi/pip
|
||||
.. _Setuptools: https://pypi.python.org/pypi/setuptools
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _errorpages:
|
||||
|
||||
Custom Error Pages
|
||||
==================
|
||||
|
||||
|
|
@ -37,7 +39,7 @@ even if the application behaves correctly:
|
|||
|
||||
*500 Internal Server Error*
|
||||
Usually happens on programming errors or if the server is overloaded.
|
||||
A terrible good idea to have a nice page there, because your
|
||||
A terribly good idea is to have a nice page there, because your
|
||||
application *will* fail sooner or later (see also:
|
||||
:ref:`application-errors`).
|
||||
|
||||
|
|
@ -45,33 +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.
|
||||
|
||||
Here an example implementation for a "404 Page Not Found" exception::
|
||||
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.
|
||||
|
||||
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 %}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ upfront:
|
|||
- Fabric 1.0 has to be installed locally. This tutorial assumes the
|
||||
latest version of Fabric.
|
||||
- The application already has to be a package and requires a working
|
||||
`setup.py` file (:ref:`distribute-deployment`).
|
||||
:file:`setup.py` file (:ref:`distribute-deployment`).
|
||||
- In the following example we are using `mod_wsgi` for the remote
|
||||
servers. You can of course use your own favourite server there, but
|
||||
for this example we chose Apache + `mod_wsgi` because it's very easy
|
||||
|
|
@ -25,14 +25,14 @@ upfront:
|
|||
Creating the first Fabfile
|
||||
--------------------------
|
||||
|
||||
A fabfile is what controls what Fabric executes. It is named `fabfile.py`
|
||||
A fabfile is what controls what Fabric executes. It is named :file:`fabfile.py`
|
||||
and executed by the `fab` command. All the functions defined in that file
|
||||
will show up as `fab` subcommands. They are executed on one or more
|
||||
hosts. These hosts can be defined either in the fabfile or on the command
|
||||
line. In this case we will add them to the fabfile.
|
||||
|
||||
This is a basic first example that has the ability to upload the current
|
||||
sourcecode to the server and install it into a pre-existing
|
||||
source code to the server and install it into a pre-existing
|
||||
virtual environment::
|
||||
|
||||
from fabric.api import *
|
||||
|
|
@ -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
|
||||
----------------
|
||||
|
||||
|
|
@ -84,9 +73,9 @@ this command::
|
|||
$ fab pack deploy
|
||||
|
||||
However this requires that our server already has the
|
||||
``/var/www/yourapplication`` folder created and
|
||||
``/var/www/yourapplication/env`` to be a virtual environment. Furthermore
|
||||
are we not creating the configuration or `.wsgi` file on the server. So
|
||||
:file:`/var/www/yourapplication` folder created and
|
||||
:file:`/var/www/yourapplication/env` to be a virtual environment. Furthermore
|
||||
are we not creating the configuration or ``.wsgi`` file on the server. So
|
||||
how do we bootstrap a new server into our infrastructure?
|
||||
|
||||
This now depends on the number of servers we want to set up. If we just
|
||||
|
|
@ -100,22 +89,22 @@ command line::
|
|||
|
||||
To setup a new server you would roughly do these steps:
|
||||
|
||||
1. Create the directory structure in ``/var/www``::
|
||||
1. Create the directory structure in :file:`/var/www`::
|
||||
|
||||
$ mkdir /var/www/yourapplication
|
||||
$ cd /var/www/yourapplication
|
||||
$ virtualenv --distribute env
|
||||
|
||||
2. Upload a new `application.wsgi` file to the server and the
|
||||
configuration file for the application (eg: `application.cfg`)
|
||||
2. Upload a new :file:`application.wsgi` file to the server and the
|
||||
configuration file for the application (eg: :file:`application.cfg`)
|
||||
|
||||
3. Create a new Apache config for `yourapplication` and activate it.
|
||||
Make sure to activate watching for changes of the `.wsgi` file so
|
||||
3. Create a new Apache config for ``yourapplication`` and activate it.
|
||||
Make sure to activate watching for changes of the ``.wsgi`` file so
|
||||
that we can automatically reload the application by touching it.
|
||||
(See :ref:`mod_wsgi-deployment` for more information)
|
||||
|
||||
So now the question is, where do the `application.wsgi` and
|
||||
`application.cfg` files come from?
|
||||
So now the question is, where do the :file:`application.wsgi` and
|
||||
:file:`application.cfg` files come from?
|
||||
|
||||
The WSGI File
|
||||
-------------
|
||||
|
|
@ -142,7 +131,7 @@ The Configuration File
|
|||
----------------------
|
||||
|
||||
Now as mentioned above, the application will find the correct
|
||||
configuration file by looking up the `YOURAPPLICATION_CONFIG` environment
|
||||
configuration file by looking up the ``YOURAPPLICATION_CONFIG`` environment
|
||||
variable. So we have to put the configuration in a place where the
|
||||
application will able to find it. Configuration files have the unfriendly
|
||||
quality of being different on all computers, so you do not version them
|
||||
|
|
@ -151,11 +140,12 @@ usually.
|
|||
A popular approach is to store configuration files for different servers
|
||||
in a separate version control repository and check them out on all
|
||||
servers. Then symlink the file that is active for the server into the
|
||||
location where it's expected (eg: ``/var/www/yourapplication``).
|
||||
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
|
||||
----------------
|
||||
|
||||
|
|
@ -168,7 +158,7 @@ can pack up the application and deploy it::
|
|||
Fabric will now connect to all servers and run the commands as written
|
||||
down in the fabfile. First it will execute pack so that we have our
|
||||
tarball ready and then it will execute deploy and upload the source code
|
||||
to all servers and install it there. Thanks to the `setup.py` file we
|
||||
to all servers and install it there. Thanks to the :file:`setup.py` file we
|
||||
will automatically pull in the required libraries into our virtual
|
||||
environment.
|
||||
|
||||
|
|
@ -186,11 +176,11 @@ deployment actually fun:
|
|||
out the latest version on the server and then install. That way you
|
||||
can also easily go back to older versions.
|
||||
- hook in testing functionality so that you can deploy to an external
|
||||
server and run the testsuite.
|
||||
server and run the test suite.
|
||||
|
||||
Working with Fabric is fun and you will notice that it's quite magical to
|
||||
type ``fab deploy`` and see your application being deployed automatically
|
||||
to one or more remote servers.
|
||||
|
||||
|
||||
.. _Fabric: http://fabfile.org/
|
||||
.. _Fabric: http://www.fabfile.org/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Adding a favicon
|
|||
A "favicon" is an icon used by browsers for tabs and bookmarks. This helps
|
||||
to distinguish your website and to give it a unique brand.
|
||||
|
||||
A common question is how to add a favicon to a flask application. First, of
|
||||
A common question is how to add a favicon to a Flask application. First, of
|
||||
course, you need an icon. It should be 16 × 16 pixels and in the ICO file
|
||||
format. This is not a requirement but a de-facto standard supported by all
|
||||
relevant browsers. Put the icon in your static directory as
|
||||
|
|
@ -20,7 +20,7 @@ tag in your HTML. So, for example:
|
|||
That's all you need for most browsers, however some really old ones do not
|
||||
support this standard. The old de-facto standard is to serve this file,
|
||||
with this name, at the website root. If your application is not mounted at
|
||||
the root path of the domain you either need to configure the webserver to
|
||||
the root path of the domain you either need to configure the web server to
|
||||
serve the icon at the root or if you can't do that you're out of luck. If
|
||||
however your application is the root you can simply route a redirect::
|
||||
|
||||
|
|
@ -44,10 +44,10 @@ same.
|
|||
|
||||
The above will serve the icon via your application and if possible it's
|
||||
better to configure your dedicated web server to serve it; refer to the
|
||||
webserver's documentation.
|
||||
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'
|
||||
|
|
@ -32,31 +32,36 @@ bootstrapping code for our application::
|
|||
|
||||
So first we need a couple of imports. Most should be straightforward, the
|
||||
:func:`werkzeug.secure_filename` is explained a little bit later. The
|
||||
`UPLOAD_FOLDER` is where we will store the uploaded files and the
|
||||
`ALLOWED_EXTENSIONS` is the set of allowed file extensions. Then we add a
|
||||
URL rule by hand to the application. Now usually we're not doing that, so
|
||||
why here? The reasons is that we want the webserver (or our development
|
||||
server) to serve these files for us and so we only need a rule to generate
|
||||
the URL to these files.
|
||||
``UPLOAD_FOLDER`` is where we will store the uploaded files and the
|
||||
``ALLOWED_EXTENSIONS`` is the set of allowed file extensions.
|
||||
|
||||
Why do we limit the extensions that are allowed? You probably don't want
|
||||
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? :)
|
||||
:ref:`xss`). Also make sure to disallow ``.php`` files if the server
|
||||
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():
|
||||
if request.method == 'POST':
|
||||
# check if the post request has the file part
|
||||
if 'file' not in request.files:
|
||||
flash('No file part')
|
||||
return redirect(request.url)
|
||||
file = request.files['file']
|
||||
# if user does not select file, browser also
|
||||
# submit an empty part without filename
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(request.url)
|
||||
if file and allowed_file(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
|
|
@ -66,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>
|
||||
'''
|
||||
|
||||
|
|
@ -89,7 +94,7 @@ before storing it directly on the filesystem.
|
|||
filename = "../../../../home/username/.bashrc"
|
||||
|
||||
Assuming the number of ``../`` is correct and you would join this with
|
||||
the `UPLOAD_FOLDER` the user might have the ability to modify a file on
|
||||
the ``UPLOAD_FOLDER`` the user might have the ability to modify a file on
|
||||
the server's filesystem he or she should not modify. This does require some
|
||||
knowledge about how the application looks like, but trust me, hackers
|
||||
are patient :)
|
||||
|
|
@ -99,8 +104,11 @@ 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. As of
|
||||
Flask 0.5 we can use a function that does that for us::
|
||||
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::
|
||||
|
||||
from flask import send_from_directory
|
||||
|
||||
|
|
@ -160,22 +168,17 @@ client asks the server every 5 seconds how much it has transmitted
|
|||
already. Do you realize the irony? The client is asking for something it
|
||||
should already know.
|
||||
|
||||
Now there are better solutions to that work faster and more reliable. The
|
||||
web changed a lot lately and you can use HTML5, Java, Silverlight or Flash
|
||||
to get a nicer uploading experience on the client side. Look at the
|
||||
following libraries for some nice examples how to do that:
|
||||
|
||||
- `Plupload <http://www.plupload.com/>`_ - HTML5, Java, Flash
|
||||
- `SWFUpload <http://www.swfupload.org/>`_ - Flash
|
||||
- `JumpLoader <http://jumploader.com/>`_ - Java
|
||||
|
||||
|
||||
An Easier Solution
|
||||
------------------
|
||||
|
||||
Because the common pattern for file uploads exists almost unchanged in all
|
||||
applications dealing with uploads, there is a Flask extension called
|
||||
`Flask-Uploads`_ that implements a full fledged upload mechanism with
|
||||
white and blacklisting of extensions and more.
|
||||
Now there are better solutions that work faster and are more reliable. There
|
||||
are JavaScript libraries like jQuery_ that have form plugins to ease the
|
||||
construction of progress bar.
|
||||
|
||||
.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/
|
||||
Because the common pattern for file uploads exists almost unchanged in all
|
||||
applications dealing with uploads, there is also a Flask extension called
|
||||
`Flask-Uploads`_ that implements a full fledged upload mechanism with white and
|
||||
blacklisting of extensions and more.
|
||||
|
||||
.. _jQuery: https://jquery.com/
|
||||
.. _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
|
||||
---------------
|
||||
|
|
@ -38,11 +40,7 @@ So here is a full example::
|
|||
return redirect(url_for('index'))
|
||||
return render_template('login.html', error=error)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
||||
|
||||
And here the ``layout.html`` template which does the magic:
|
||||
And here is the :file:`layout.html` template which does the magic:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -59,7 +57,7 @@ And here the ``layout.html`` template which does the magic:
|
|||
{% endwith %}
|
||||
{% block body %}{% endblock %}
|
||||
|
||||
And here the index.html template:
|
||||
Here is the :file:`index.html` template which inherits from :file:`layout.html`:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -69,7 +67,8 @@ And here the index.html template:
|
|||
<p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
|
||||
{% endblock %}
|
||||
|
||||
And of course the login template:
|
||||
And here is the :file:`login.html` template which also inherits from
|
||||
:file:`layout.html`:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -79,7 +78,7 @@ And of course the login template:
|
|||
{% 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
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ Loading jQuery
|
|||
In order to use jQuery, you have to download it first and place it in the
|
||||
static folder of your application and then ensure it's loaded. Ideally
|
||||
you have a layout template that is used for all pages where you just have
|
||||
to add a script statement to the bottom of your `<body>` to load jQuery:
|
||||
to add a script statement to the bottom of your ``<body>`` to load jQuery:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
|
|
@ -65,12 +65,12 @@ like this:
|
|||
|
||||
The ``|safe`` is necessary in Flask before 0.10 so that Jinja does not
|
||||
escape the JSON encoded string with HTML rules. Usually this would be
|
||||
necessary, but we are inside a `script` block here where different rules
|
||||
necessary, but we are inside a ``script`` block here where different rules
|
||||
apply.
|
||||
|
||||
.. admonition:: Information for Pros
|
||||
|
||||
In HTML the `script` tag is declared `CDATA` which means that entities
|
||||
In HTML the ``script`` tag is declared ``CDATA`` which means that entities
|
||||
will not be parsed. Everything until ``</script>`` is handled as script.
|
||||
This also means that there must never be any ``</`` between the script
|
||||
tags. ``|tojson`` is kind enough to do the right thing here and
|
||||
|
|
@ -119,9 +119,9 @@ special error reporting in that case.
|
|||
The HTML
|
||||
--------
|
||||
|
||||
Your index.html template either has to extend a `layout.html` template with
|
||||
Your index.html template either has to extend a :file:`layout.html` template with
|
||||
jQuery loaded and the `$SCRIPT_ROOT` variable set, or do that on the top.
|
||||
Here's the HTML code needed for our little application (`index.html`).
|
||||
Here's the HTML code needed for our little application (:file:`index.html`).
|
||||
Notice that we also drop the script directly into the HTML here. It is
|
||||
usually a better idea to have that in a separate script file:
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ explanation of the little bit of code above:
|
|||
when the user clicked on the element. If that function returns
|
||||
`false`, the default behavior will not kick in (in this case, navigate
|
||||
to the `#` URL).
|
||||
4. ``$.getJSON(url, data, func)`` sends a `GET` request to `url` and will
|
||||
4. ``$.getJSON(url, data, func)`` sends a ``GET`` request to `url` and will
|
||||
send the contents of the `data` object as query parameters. Once the
|
||||
data arrived, it will call the given function with the return value as
|
||||
argument. Note that we can use the `$SCRIPT_ROOT` variable here that
|
||||
|
|
@ -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
|
||||
<http://github.com/mitsuhiko/flask/tree/master/examples/jqueryexample>`_
|
||||
from github.
|
||||
<https://github.com/pallets/flask/tree/master/examples/jqueryexample>`_
|
||||
from GitHub.
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ Imagine the current application looks somewhat like this::
|
|||
def user(username):
|
||||
pass
|
||||
|
||||
Then the centralized approach you would have one file with the views
|
||||
(`views.py`) but without any decorator::
|
||||
Then, with the centralized approach you would have one file with the views
|
||||
(:file:`views.py`) but without any decorator::
|
||||
|
||||
def index():
|
||||
pass
|
||||
|
|
@ -54,7 +54,7 @@ Loading Late
|
|||
------------
|
||||
|
||||
So far we only split up the views and the routing, but the module is still
|
||||
loaded upfront. The trick to actually load the view function as needed.
|
||||
loaded upfront. The trick is to actually load the view function as needed.
|
||||
This can be accomplished with a helper class that behaves just like a
|
||||
function but internally imports the real function on first use::
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Declarative
|
|||
The default behavior of MongoKit is the declarative one that is based on
|
||||
common ideas from Django or the SQLAlchemy declarative extension.
|
||||
|
||||
Here an example `app.py` module for your application::
|
||||
Here an example :file:`app.py` module for your application::
|
||||
|
||||
from flask import Flask
|
||||
from mongokit import Connection, Document
|
||||
|
|
@ -47,13 +47,16 @@ MongoDB is schemaless. This means you can modify the data structure from one
|
|||
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 `app.py`, e.g.)::
|
||||
Here is an example document (put this also into :file:`app.py`, e.g.)::
|
||||
|
||||
from mongokit import ValidationError
|
||||
|
||||
def max_length(length):
|
||||
def validate(value):
|
||||
if len(value) <= length:
|
||||
return True
|
||||
raise Exception('%s must be at most %s characters long' % length)
|
||||
# must have %s in error format string to have mongokit place key in there
|
||||
raise ValidationError('%s must be at most {} characters long'.format(length))
|
||||
return validate
|
||||
|
||||
class User(Document):
|
||||
|
|
@ -76,7 +79,7 @@ Here is an example document (put this also into `app.py`, e.g.)::
|
|||
This example shows you how to define your schema (named structure), a
|
||||
validator for the maximum character length and uses a special MongoKit feature
|
||||
called `use_dot_notation`. Per default MongoKit behaves like a python
|
||||
dictionary but with `use_dot_notation` set to `True` you can use your
|
||||
dictionary but with `use_dot_notation` set to ``True`` you can use your
|
||||
documents like you use models in nearly any other ORM by using dots to
|
||||
separate between attributes.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,30 +8,34 @@ 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
|
||||
---------------
|
||||
|
||||
To convert that into a larger one, just create a new folder
|
||||
`yourapplication` inside the existing one and move everything below it.
|
||||
Then rename `yourapplication.py` to `__init__.py`. (Make sure to delete
|
||||
all `.pyc` files first, otherwise things would most likely break)
|
||||
:file:`yourapplication` inside the existing one and move everything below it.
|
||||
Then rename :file:`yourapplication.py` to :file:`__init__.py`. (Make sure to delete
|
||||
all ``.pyc`` files first, otherwise things would most likely break)
|
||||
|
||||
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,32 +45,57 @@ 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 `runserver.py` next to the inner
|
||||
`yourapplication` folder with the following contents::
|
||||
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
|
||||
following quick checklist:
|
||||
|
||||
1. the `Flask` application object creation has to be in the
|
||||
`__init__.py` file. That way each module can import it safely and the
|
||||
:file:`__init__.py` file. That way each module can import it safely and the
|
||||
`__name__` variable will resolve to the correct package.
|
||||
2. all the view functions (the ones with a :meth:`~flask.Flask.route`
|
||||
decorator on top) have to be imported in the `__init__.py` file.
|
||||
decorator on top) have to be imported in the :file:`__init__.py` file.
|
||||
Not the object itself, but the module it is in. Import the view module
|
||||
**after the application object is created**.
|
||||
|
||||
Here's an example `__init__.py`::
|
||||
Here's an example :file:`__init__.py`::
|
||||
|
||||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
import yourapplication.views
|
||||
|
||||
And this is what `views.py` would look like::
|
||||
And this is what :file:`views.py` would look like::
|
||||
|
||||
from yourapplication import app
|
||||
|
||||
|
|
@ -77,12 +106,12 @@ And this is what `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
|
||||
|
|
@ -93,9 +122,9 @@ You should then end up with something like that::
|
|||
|
||||
Every Python programmer hates them, and yet we just added some:
|
||||
circular imports (That's when two modules depend on each other. In this
|
||||
case `views.py` depends on `__init__.py`). Be advised that this is a
|
||||
case :file:`views.py` depends on :file:`__init__.py`). Be advised that this is a
|
||||
bad idea in general but here it is actually fine. The reason for this is
|
||||
that we are not actually using the views in `__init__.py` and just
|
||||
that we are not actually using the views in :file:`__init__.py` and just
|
||||
ensuring the module is imported and we are doing that at the bottom of
|
||||
the file.
|
||||
|
||||
|
|
@ -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
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ there is a Flask extension that handles that for you. This is recommended
|
|||
if you want to get started quickly.
|
||||
|
||||
You can download `Flask-SQLAlchemy`_ from `PyPI
|
||||
<http://pypi.python.org/pypi/Flask-SQLAlchemy>`_.
|
||||
<https://pypi.python.org/pypi/Flask-SQLAlchemy>`_.
|
||||
|
||||
.. _Flask-SQLAlchemy: http://packages.python.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 `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
|
||||
|
|
@ -70,7 +70,7 @@ when the application shuts down::
|
|||
def shutdown_session(exception=None):
|
||||
db_session.remove()
|
||||
|
||||
Here is an example model (put this into `models.py`, e.g.)::
|
||||
Here is an example model (put this into :file:`models.py`, e.g.)::
|
||||
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from yourapplication.database import Base
|
||||
|
|
@ -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://www.sqlalchemy.org/docs/orm/extensions/declarative.html
|
||||
https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
|
||||
|
||||
Manual Object Relational Mapping
|
||||
--------------------------------
|
||||
|
|
@ -122,7 +122,7 @@ flexible but a little more to type. In general it works like the
|
|||
declarative approach, so make sure to also split up your application into
|
||||
multiple modules in a package.
|
||||
|
||||
Here is an example `database.py` module for your application::
|
||||
Here is an example :file:`database.py` module for your application::
|
||||
|
||||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
|
@ -135,7 +135,7 @@ Here is an example `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::
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ application module::
|
|||
def shutdown_session(exception=None):
|
||||
db_session.remove()
|
||||
|
||||
Here is an example table and model (put this into `models.py`)::
|
||||
Here is an example table and model (put this into :file:`models.py`)::
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, String
|
||||
from sqlalchemy.orm import mapper
|
||||
|
|
@ -177,7 +177,7 @@ SQL Abstraction Layer
|
|||
If you just want to use the database system (and SQL) abstraction layer
|
||||
you basically only need the engine::
|
||||
|
||||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy import create_engine, MetaData, Table
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
|
||||
metadata = MetaData(bind=engine)
|
||||
|
|
@ -186,7 +186,7 @@ Then you can either declare the tables in your code like in the examples
|
|||
above, or automatically load them::
|
||||
|
||||
from sqlalchemy import Table
|
||||
|
||||
|
||||
users = Table('users', metadata, autoload=True)
|
||||
|
||||
To insert data you can use the `insert` method. We have to get a
|
||||
|
|
@ -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://sqlalchemy.org/>`_.
|
||||
`website <https://www.sqlalchemy.org/>`_.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
Using SQLite 3 with Flask
|
||||
=========================
|
||||
|
||||
In Flask you can implement the opening of database connections on demand
|
||||
and closing it when the context dies (usually at the end of the request)
|
||||
easily.
|
||||
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::
|
||||
|
||||
|
|
@ -26,12 +26,11 @@ Here is a simple example of how you can use SQLite 3 with Flask::
|
|||
if db is not None:
|
||||
db.close()
|
||||
|
||||
All the application needs to do in order to now use the database is having
|
||||
an active application context (which is always true if there is an request
|
||||
in flight) or to create an application context itself. At that point the
|
||||
``get_db`` function can be used to get the current database connection.
|
||||
Whenever the context is destroyed the database connection will be
|
||||
terminated.
|
||||
Now, to use the database, the application must either have an active
|
||||
application context (which is always true if there is a request in flight)
|
||||
or create an application context itself. At that point the ``get_db``
|
||||
function can be used to get the current database connection. Whenever the
|
||||
context is destroyed the database connection will be terminated.
|
||||
|
||||
Note: if you use Flask 0.9 or older you need to use
|
||||
``flask._app_ctx_stack.top`` instead of ``g`` as the :data:`flask.g`
|
||||
|
|
@ -56,7 +55,7 @@ Connect on Demand
|
|||
-----------------
|
||||
|
||||
The upside of this approach (connecting on first use) is that this will
|
||||
only opening the connection if truly necessary. If you want to use this
|
||||
only open the connection if truly necessary. If you want to use this
|
||||
code outside a request context you can use it in a Python shell by opening
|
||||
the application context by hand::
|
||||
|
||||
|
|
@ -71,8 +70,9 @@ Easy Querying
|
|||
Now in each request handling function you can access `g.db` to get the
|
||||
current open database connection. To simplify working with SQLite, a
|
||||
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``::
|
||||
from the database to convert the result. For instance, in order to get
|
||||
dictionaries instead of tuples, this could be inserted into the ``get_db``
|
||||
function we created above::
|
||||
|
||||
def make_dicts(cursor, row):
|
||||
return dict((cursor.description[idx][0], value)
|
||||
|
|
@ -80,22 +80,38 @@ 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
|
||||
|
||||
Additionally it is a good idea to provide a query function that combines
|
||||
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 raw
|
||||
cursor and connection objects.
|
||||
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::
|
||||
|
||||
|
|
@ -115,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
|
||||
---------------
|
||||
|
|
@ -132,7 +148,7 @@ can do that for you::
|
|||
db.cursor().executescript(f.read())
|
||||
db.commit()
|
||||
|
||||
You can then create such a database from the python shell:
|
||||
You can then create such a database from the Python shell:
|
||||
|
||||
>>> from yourapplication import init_db
|
||||
>>> init_db()
|
||||
|
|
|
|||
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.
|
||||
|
|
@ -14,7 +14,7 @@ with an example.
|
|||
Base Template
|
||||
-------------
|
||||
|
||||
This template, which we'll call ``layout.html``, defines a simple HTML skeleton
|
||||
This template, which we'll call :file:`layout.html`, defines a simple HTML skeleton
|
||||
document that you might use for a simple two-column page. It's the job of
|
||||
"child" templates to fill the empty blocks with content:
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ View Decorators
|
|||
===============
|
||||
|
||||
Python has a really interesting feature called function decorators. This
|
||||
allow some really neat things for web applications. Because each view in
|
||||
Flask is a function decorators can be used to inject additional
|
||||
allows some really neat things for web applications. Because each view in
|
||||
Flask is a function, decorators can be used to inject additional
|
||||
functionality to one or more functions. The :meth:`~flask.Flask.route`
|
||||
decorator is the one you probably used already. But there are use cases
|
||||
for implementing your own decorator. For instance, imagine you have a
|
||||
view that should only be used by people that are logged in to. If a user
|
||||
view that should only be used by people that are logged in. If a user
|
||||
goes to the site and is not logged in, they should be redirected to the
|
||||
login page. This is a good example of a use case where a decorator is an
|
||||
excellent solution.
|
||||
|
|
@ -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
|
||||
-----------------
|
||||
|
||||
|
|
@ -54,7 +61,7 @@ because of that you would like to cache the generated results for a
|
|||
certain amount of time. A decorator would be nice for that. We're
|
||||
assuming you have set up a cache like mentioned in :ref:`caching-pattern`.
|
||||
|
||||
Here an example cache function. It generates the cache key from a
|
||||
Here is an example cache function. It generates the cache key from a
|
||||
specific prefix (actually a format string) and the current path of the
|
||||
request. Notice that we are using a function that first creates the
|
||||
decorator that then decorates the function. Sounds awful? Unfortunately
|
||||
|
|
@ -120,14 +127,14 @@ As you can see, if no template name is provided it will use the endpoint
|
|||
of the URL map with dots converted to slashes + ``'.html'``. Otherwise
|
||||
the provided template name is used. When the decorated function returns,
|
||||
the dictionary returned is passed to the template rendering function. If
|
||||
`None` is returned, an empty dictionary is assumed, if something else than
|
||||
``None`` is returned, an empty dictionary is assumed, if something else than
|
||||
a dictionary is returned we return it from the function unchanged. That
|
||||
way you can still use the redirect function or return simple strings.
|
||||
|
||||
Here the code for that decorator::
|
||||
Here is the code for that decorator::
|
||||
|
||||
from functools import wraps
|
||||
from flask import request
|
||||
from flask import request, render_template
|
||||
|
||||
def templated(template=None):
|
||||
def decorator(f):
|
||||
|
|
@ -151,18 +158,15 @@ Endpoint Decorator
|
|||
------------------
|
||||
|
||||
When you want to use the werkzeug routing system for more flexibility you
|
||||
need to map the endpoint as defined in the :class:`~werkzeug.routing.Rule`
|
||||
to a view function. This is possible with this decorator. For example::
|
||||
need to map the endpoint as defined in the :class:`~werkzeug.routing.Rule`
|
||||
to a view function. This is possible with this decorator. For example::
|
||||
|
||||
from flask import Flask
|
||||
from werkzeug.routing import Rule
|
||||
|
||||
app = Flask(__name__)
|
||||
app.url_map.add(Rule('/', endpoint='index'))
|
||||
|
||||
@app.endpoint('index')
|
||||
def my_index():
|
||||
return "Hello world"
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.url_map.add(Rule('/', endpoint='index'))
|
||||
|
||||
@app.endpoint('index')
|
||||
def my_index():
|
||||
return "Hello world"
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
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
|
||||
forms, you might want to give it a try.
|
||||
|
||||
When you are working with WTForms you have to define your forms as classes
|
||||
first. I recommend breaking up the application into multiple modules
|
||||
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
|
||||
<http://pypi.python.org/pypi/Flask-WTF>`_.
|
||||
<https://pypi.python.org/pypi/Flask-WTF>`_.
|
||||
|
||||
.. _Flask-WTF: http://packages.python.org/Flask-WTF/
|
||||
.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/
|
||||
|
||||
The Forms
|
||||
---------
|
||||
|
||||
This is an example form for a typical registration page::
|
||||
|
||||
from wtforms import Form, BooleanField, TextField, PasswordField, validators
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators
|
||||
|
||||
class RegistrationForm(Form):
|
||||
username = TextField('Username', [validators.Length(min=4, max=25)])
|
||||
email = TextField('Email Address', [validators.Length(min=6, max=35)])
|
||||
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,30 +54,30 @@ 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:
|
||||
|
||||
1. create the form from the request :attr:`~flask.request.form` value if
|
||||
the data is submitted via the HTTP `POST` method and
|
||||
:attr:`~flask.request.args` if the data is submitted as `GET`.
|
||||
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
|
||||
label and a list of errors if there are any.
|
||||
|
||||
Here's an example `_formhelpers.html` template with such a macro:
|
||||
Here's an example :file:`_formhelpers.html` template with such a macro:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -95,20 +95,20 @@ Here's an example `_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
|
||||
with the `|safe` filter.
|
||||
strings, so we have to tell Jinja2 that this data is already HTML-escaped
|
||||
with the ``|safe`` filter.
|
||||
|
||||
Here the `register.html` template for the function we used above which
|
||||
takes advantage of the `_formhelpers.html` template:
|
||||
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 `_formhelpers.html` template:
|
|||
For more information about WTForms, head over to the `WTForms
|
||||
website`_.
|
||||
|
||||
.. _WTForms: http://wtforms.simplecodes.com/
|
||||
.. _WTForms website: http://wtforms.simplecodes.com/
|
||||
.. _WTForms: https://wtforms.readthedocs.io/
|
||||
.. _WTForms website: https://wtforms.readthedocs.io/
|
||||
|
|
|
|||
|
|
@ -3,59 +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.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
You need to use Python 3.3 or higher. 3.2 and older are *not* supported.
|
||||
|
||||
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 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.
|
||||
|
||||
In addition to that you need to use the latest and greatest versions of
|
||||
`itsdangerous`, `Jinja2` and `Werkzeug`.
|
||||
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.
|
||||
|
||||
API Stability
|
||||
-------------
|
||||
|
||||
Some of the decisions made in regards to unicode and byte untilization 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.
|
||||
|
||||
A lot of the documentation out there on using WSGI leaves out those
|
||||
details as it was written before WSGI was updated to Python 3. While the
|
||||
API for Werkzeug and Flask on Python 2.x should not change much we cannot
|
||||
guarantee that this won't happen on Python 3.
|
||||
|
||||
Few Users
|
||||
---------
|
||||
|
||||
Python 3 currently has less than 1% of the users of Python 2 going by PyPI
|
||||
download stats. As a result many of the problems you will encounter are
|
||||
probably hard to search for on the internet if they are Python 3 specific.
|
||||
|
||||
Small Ecosystem
|
||||
---------------
|
||||
|
||||
The majority of the Flask extensions, all of the documentation and the
|
||||
vast majority of the PyPI provided libraries do not support Python 3 yet.
|
||||
Even if you start your project with knowing that all you will need is
|
||||
supported by Python 3 you don't know what happens six months from now. If
|
||||
you are adventurous you can start porting libraries on your own, but that
|
||||
is nothing for the faint of heart.
|
||||
|
||||
Recommendations
|
||||
---------------
|
||||
|
||||
Unless you are already familiar with the differences in the versions we
|
||||
recommend sticking to current versions of Python until the ecosystem
|
||||
caught up.
|
||||
|
||||
The majority of the upgrade pain is in the lower-level libararies 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.
|
||||
|
|
|
|||
|
|
@ -18,30 +18,15 @@ A minimal Flask application looks something like this::
|
|||
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
return 'Hello World!'
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
Just save it as `hello.py` (or something similar) and run it with your Python
|
||||
interpreter. Make sure to not call your application `flask.py` because this
|
||||
would conflict with Flask itself.
|
||||
|
||||
::
|
||||
|
||||
$ python hello.py
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
Now head over to `http://127.0.0.1:5000/ <http://127.0.0.1:5000/>`_, and you
|
||||
should see your hello world greeting.
|
||||
return 'Hello, World!'
|
||||
|
||||
So what did that code do?
|
||||
|
||||
1. First we imported the :class:`~flask.Flask` class. An instance of this
|
||||
class will be our WSGI application.
|
||||
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
|
||||
|
|
@ -51,12 +36,34 @@ So what did that code do?
|
|||
4. The function is given a name which is also used to generate URLs for that
|
||||
particular function, and returns the message we want to display in the
|
||||
user's browser.
|
||||
5. Finally we use the :meth:`~flask.Flask.run` function to run the local server
|
||||
with our application. The ``if __name__ == '__main__':`` makes sure the
|
||||
server only runs if the script is executed directly from the Python
|
||||
interpreter and not used as an imported module.
|
||||
|
||||
To stop the server, hit control-C.
|
||||
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 ``-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::
|
||||
|
||||
$ export FLASK_APP=hello.py
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
If you are on Windows you need to use ``set`` instead of ``export``.
|
||||
|
||||
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
|
||||
but probably not what you want to use in production. For deployment options see
|
||||
:ref:`deployment`.
|
||||
|
||||
Now head over to `http://127.0.0.1:5000/ <http://127.0.0.1:5000/>`_, and you
|
||||
should see your hello world greeting.
|
||||
|
||||
.. _public-server:
|
||||
|
||||
|
|
@ -67,37 +74,70 @@ To stop the server, hit control-C.
|
|||
default because in debugging mode a user of the application can execute
|
||||
arbitrary Python code on your computer.
|
||||
|
||||
If you have `debug` disabled or trust the users on your network, you can
|
||||
make the server publicly available simply by changing the call of the
|
||||
:meth:`~flask.Flask.run` method to look like this::
|
||||
If you have the debugger disabled or trust the users on your network,
|
||||
you can make the server publicly available simply by adding
|
||||
``--host=0.0.0.0`` to the command line::
|
||||
|
||||
app.run(host='0.0.0.0')
|
||||
flask run --host=0.0.0.0
|
||||
|
||||
This tells your operating system to listen on all public IPs.
|
||||
|
||||
|
||||
What to do if the Server does not Start
|
||||
---------------------------------------
|
||||
|
||||
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 0.11 use to have different ways to start the
|
||||
application. In short, the :command:`flask` command did not exist, and
|
||||
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.
|
||||
|
||||
Invalid Import Name
|
||||
```````````````````
|
||||
|
||||
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.
|
||||
|
||||
.. _debug-mode:
|
||||
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
The :meth:`~flask.Flask.run` method 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.
|
||||
(Want to just log errors and stack traces? See :ref:`application-errors`)
|
||||
|
||||
There are two ways to enable debugging. Either set that flag on the
|
||||
application object::
|
||||
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.
|
||||
|
||||
app.debug = True
|
||||
app.run()
|
||||
To enable debug mode you can export the ``FLASK_DEBUG`` environment variable
|
||||
before running the server::
|
||||
|
||||
Or pass it as a parameter to run::
|
||||
$ export FLASK_DEBUG=1
|
||||
$ flask run
|
||||
|
||||
app.run(debug=True)
|
||||
(On Windows you need to use ``set`` instead of ``export``).
|
||||
|
||||
Both methods have the exact same effect.
|
||||
This does the following things:
|
||||
|
||||
1. it activates the debugger
|
||||
2. it activates the automatic reloader
|
||||
3. it enables the debug mode on the Flask application.
|
||||
|
||||
There are more parameters that are explained in the :ref:`server` docs.
|
||||
|
||||
.. admonition:: Attention
|
||||
|
||||
|
|
@ -119,14 +159,11 @@ 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():
|
||||
|
|
@ -134,18 +171,18 @@ bind a function to a URL. Here are some basic examples::
|
|||
|
||||
@app.route('/hello')
|
||||
def hello():
|
||||
return 'Hello World'
|
||||
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):
|
||||
|
|
@ -157,108 +194,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
|
||||
|
||||
=========== ===========================================
|
||||
`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 file system. 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:
|
||||
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.
|
||||
|
||||
>>> 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
|
||||
Why would you want to build URLs using the URL reversing function
|
||||
:func:`~flask.url_for` instead of hard-coding them into your templates?
|
||||
|
||||
(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`).
|
||||
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.
|
||||
|
||||
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:
|
||||
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`. ::
|
||||
|
||||
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.
|
||||
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():
|
||||
|
|
@ -267,64 +307,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
|
||||
------------
|
||||
|
|
@ -332,14 +319,14 @@ Static Files
|
|||
Dynamic web applications also need static files. That's usually where
|
||||
the CSS and JavaScript files are coming from. Ideally your web server is
|
||||
configured to serve them for you, but during development Flask can do that
|
||||
as well. Just create a folder called `static` in your package or next to
|
||||
your module and it will be available at `/static` on the application.
|
||||
as well. Just create a folder called :file:`static` in your package or next to
|
||||
your module and it will be available at ``/static`` on the application.
|
||||
|
||||
To generate URLs for static files, use the special ``'static'`` endpoint name::
|
||||
|
||||
url_for('static', filename='style.css')
|
||||
|
||||
The file has to be stored on the filesystem as ``static/style.css``.
|
||||
The file has to be stored on the filesystem as :file:`static/style.css`.
|
||||
|
||||
Rendering Templates
|
||||
-------------------
|
||||
|
|
@ -347,7 +334,7 @@ Rendering Templates
|
|||
Generating HTML from within Python is not fun, and actually pretty
|
||||
cumbersome because you have to do the HTML escaping on your own to keep
|
||||
the application secure. Because of that Flask configures the `Jinja2
|
||||
<http://jinja.pocoo.org/2/>`_ template engine for you automatically.
|
||||
<http://jinja.pocoo.org/>`_ template engine for you automatically.
|
||||
|
||||
To render a template you can use the :func:`~flask.render_template`
|
||||
method. All you have to do is provide the name of the template and the
|
||||
|
|
@ -361,7 +348,7 @@ Here's a simple example of how to render a template::
|
|||
def hello(name=None):
|
||||
return render_template('hello.html', name=name)
|
||||
|
||||
Flask will look for templates in the `templates` folder. So if your
|
||||
Flask will look for templates in the :file:`templates` folder. So if your
|
||||
application is a module, this folder is next to that module, if it's a
|
||||
package it's actually inside your package:
|
||||
|
||||
|
|
@ -380,7 +367,7 @@ package it's actually inside your package:
|
|||
|
||||
For templates you can use the full power of Jinja2 templates. Head over
|
||||
to the official `Jinja2 Template Documentation
|
||||
<http://jinja.pocoo.org/2/documentation/templates>`_ for more information.
|
||||
<http://jinja.pocoo.org/docs/templates>`_ for more information.
|
||||
|
||||
Here is an example template:
|
||||
|
||||
|
|
@ -391,7 +378,7 @@ Here is an example template:
|
|||
{% if name %}
|
||||
<h1>Hello {{ name }}!</h1>
|
||||
{% else %}
|
||||
<h1>Hello World!</h1>
|
||||
<h1>Hello, World!</h1>
|
||||
{% endif %}
|
||||
|
||||
Inside templates you also have access to the :class:`~flask.request`,
|
||||
|
|
@ -403,22 +390,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
|
||||
|
||||
|
|
@ -436,7 +423,7 @@ u'Marked up \xbb HTML'
|
|||
Accessing Request Data
|
||||
----------------------
|
||||
|
||||
For web applications it's crucial to react to the data a client sent to
|
||||
For web applications it's crucial to react to the data a client sends to
|
||||
the server. In Flask this information is provided by the global
|
||||
:class:`~flask.request` object. If you have some experience with Python
|
||||
you might be wondering how that object can be global and how Flask
|
||||
|
|
@ -472,7 +459,7 @@ will notice that code which depends on a request object will suddenly break
|
|||
because there is no request object. The solution is creating a request
|
||||
object yourself and binding it to the context. The easiest solution for
|
||||
unit testing is to use the :meth:`~flask.Flask.test_request_context`
|
||||
context manager. In combination with the `with` statement it will bind a
|
||||
context manager. In combination with the ``with`` statement it will bind a
|
||||
test request so that you can interact with it. Here is an example::
|
||||
|
||||
from flask import request
|
||||
|
|
@ -495,16 +482,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
|
||||
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.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
|
||||
attributes mentioned above::
|
||||
|
||||
@app.route('/login', methods=['POST', 'GET'])
|
||||
|
|
@ -520,23 +507,23 @@ 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', '')
|
||||
|
||||
We recommend accessing URL parameters with `get` or by catching the
|
||||
`KeyError` because users might change the URL and presenting them a 400
|
||||
:exc:`KeyError` because users might change the URL and presenting them a 400
|
||||
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
|
||||
|
|
@ -573,7 +560,7 @@ pass it through the :func:`~werkzeug.utils.secure_filename` function that
|
|||
Werkzeug provides for you::
|
||||
|
||||
from flask import request
|
||||
from werkzeug import secure_filename
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
@app.route('/upload', methods=['GET', 'POST'])
|
||||
def upload_file():
|
||||
|
|
@ -662,6 +649,8 @@ Note the ``404`` after the :func:`~flask.render_template` call. This
|
|||
tells Flask that the status code of that page should be 404 which means
|
||||
not found. By default 200 is assumed which translates to: all went well.
|
||||
|
||||
See :ref:`error-handlers` for more details.
|
||||
|
||||
.. _about-responses:
|
||||
|
||||
About Responses
|
||||
|
|
@ -670,7 +659,7 @@ About Responses
|
|||
The return value from a view function is automatically converted into a
|
||||
response object for you. If the return value is a string it's converted
|
||||
into a response object with the string as response body, a ``200 OK``
|
||||
status code and a ``text/html`` mimetype. The logic that Flask applies to
|
||||
status code and a :mimetype:`text/html` mimetype. The logic that Flask applies to
|
||||
converting return values into response objects is as follows:
|
||||
|
||||
1. If a response object of the correct type is returned it's directly
|
||||
|
|
@ -680,17 +669,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):
|
||||
|
|
@ -698,9 +685,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):
|
||||
|
|
@ -739,7 +724,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>
|
||||
|
|
@ -762,13 +747,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
|
||||
|
|
@ -776,6 +761,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
|
||||
----------------
|
||||
|
|
@ -818,9 +806,11 @@ Here are some example log calls::
|
|||
|
||||
The attached :attr:`~flask.Flask.logger` is a standard logging
|
||||
:class:`~logging.Logger`, so head over to the official `logging
|
||||
documentation <http://docs.python.org/library/logging.html>`_ for more
|
||||
documentation <https://docs.python.org/library/logging.html>`_ for more
|
||||
information.
|
||||
|
||||
Read more on :ref:`application-errors`.
|
||||
|
||||
Hooking in WSGI Middlewares
|
||||
---------------------------
|
||||
|
||||
|
|
@ -832,24 +822,16 @@ can do it like this::
|
|||
from werkzeug.contrib.fixers import LighttpdCGIRootFix
|
||||
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
|
||||
|
||||
.. _quickstart_deployment:
|
||||
Using Flask Extensions
|
||||
----------------------
|
||||
|
||||
Extensions are packages that help you accomplish common tasks. For
|
||||
example, Flask-SQLAlchemy provides SQLAlchemy support that makes it simple
|
||||
and easy to use with Flask.
|
||||
|
||||
For more on Flask extensions, have a look at :ref:`extensions`.
|
||||
|
||||
Deploying to a Web Server
|
||||
-------------------------
|
||||
|
||||
Ready to deploy your new Flask app? To wrap up the quickstart, you can
|
||||
immediately deploy to a hosted platform, all of which offer a free plan for
|
||||
small projects:
|
||||
|
||||
- `Deploying Flask on Heroku <http://devcenter.heroku.com/articles/python>`_
|
||||
- `Deploying WSGI on dotCloud <http://docs.dotcloud.com/services/python/>`_
|
||||
with `Flask-specific notes <http://flask.pocoo.org/snippets/48/>`_
|
||||
|
||||
Other places where you can host your Flask app:
|
||||
|
||||
- `Deploying Flask on Webfaction <http://flask.pocoo.org/snippets/65/>`_
|
||||
- `Deploying Flask on Google App Engine <https://github.com/kamalgill/flask-appengine-template>`_
|
||||
- `Sharing your Localhost Server with Localtunnel <http://flask.pocoo.org/snippets/89/>`_
|
||||
|
||||
If you manage your own hosts and would like to host yourself, see the chapter
|
||||
on :ref:`deployment`.
|
||||
Ready to deploy your new Flask app? Go to :ref:`deployment`.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ us a :class:`~flask.ctx.RequestContext`:
|
|||
|
||||
>>> ctx = app.test_request_context('/?next=http://example.com/')
|
||||
|
||||
This context can be used in two ways. Either with the `with` statement
|
||||
This context can be used in two ways. Either with the ``with`` statement
|
||||
or by calling the :meth:`~flask.ctx.RequestContext.push` and
|
||||
:meth:`~flask.ctx.RequestContext.pop` methods:
|
||||
|
||||
|
|
@ -69,14 +69,14 @@ 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)
|
||||
|
||||
The method :meth:`~Flask.request_context` returns a new
|
||||
:class:`~flask.ctx.RequestContext` object and uses it in combination with
|
||||
the `with` statement to bind the context. Everything that is called from
|
||||
the same thread from this point onwards until the end of the `with`
|
||||
the ``with`` statement to bind the context. Everything that is called from
|
||||
the same thread from this point onwards until the end of the ``with``
|
||||
statement will have access to the request globals (:data:`flask.request`
|
||||
and others).
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -25,7 +25,7 @@ careful:
|
|||
- generating HTML without the help of Jinja2
|
||||
- calling :class:`~flask.Markup` on data submitted by users
|
||||
- sending out HTML from uploaded files, never do that, use the
|
||||
`Content-Disposition: attachment` header to prevent that problem.
|
||||
``Content-Disposition: attachment`` header to prevent that problem.
|
||||
- sending out textfiles from uploaded files. Some browsers are using
|
||||
content-type guessing based on the first few bytes so users could
|
||||
trick a browser to execute HTML.
|
||||
|
|
@ -70,10 +70,10 @@ don't keep that in mind, some people might be able to trick your
|
|||
application's users with social engineering to do stupid things without
|
||||
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
|
||||
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 ``application/json``
|
||||
mimetype if ``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.
|
||||
|
|
|
|||
52
docs/server.rst
Normal file
52
docs/server.rst
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
.. _server:
|
||||
|
||||
Development Server
|
||||
==================
|
||||
|
||||
.. currentmodule:: flask
|
||||
|
||||
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.
|
||||
|
||||
Command Line
|
||||
------------
|
||||
|
||||
The :command:`flask` command line script (:ref:`cli`) is strongly recommended for
|
||||
development because it provides a superior reload experience due to how it
|
||||
loads the application. The basic usage is like this::
|
||||
|
||||
$ 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/*.
|
||||
|
||||
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 run --no-reload
|
||||
|
||||
In Code
|
||||
-------
|
||||
|
||||
The alternative way to start the application is through the
|
||||
:meth:`Flask.run` method. This will immediately launch a local server
|
||||
exactly the same way the :command:`flask` script does.
|
||||
|
||||
Example::
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
This works well for the common case but it does not work well for
|
||||
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.
|
||||
|
|
@ -26,6 +26,16 @@ context.
|
|||
Generally it's recommended that you read the :ref:`request-context`
|
||||
chapter of the documentation first.
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
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.
|
||||
|
||||
For more information see :ref:`cli`.
|
||||
|
||||
Creating a Request Context
|
||||
--------------------------
|
||||
|
||||
|
|
@ -35,7 +45,7 @@ us a :class:`~flask.ctx.RequestContext`:
|
|||
|
||||
>>> ctx = app.test_request_context()
|
||||
|
||||
Normally you would use the `with` statement to make this request object
|
||||
Normally you would use the ``with`` statement to make this request object
|
||||
active, but in the shell it's easier to use the
|
||||
:meth:`~flask.ctx.RequestContext.push` and
|
||||
:meth:`~flask.ctx.RequestContext.pop` methods by hand:
|
||||
|
|
@ -88,6 +98,6 @@ with stuff you want to star import into your interactive session. There
|
|||
you could also define some more helper methods for common things such as
|
||||
initializing the database, dropping tables etc.
|
||||
|
||||
Just put them into a module (like `shelltools` and import from there):
|
||||
Just put them into a module (like `shelltools`) and import from there:
|
||||
|
||||
>>> from shelltools import *
|
||||
|
|
|
|||
188
docs/signals.rst
188
docs/signals.rst
|
|
@ -19,14 +19,14 @@ 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 the split of a second. These temporary
|
||||
subscribe to them for just a split second. These temporary
|
||||
subscriptions are helpful for unittesting for example. Say you want to
|
||||
know what templates were rendered as part of a request: signals allow you
|
||||
to do exactly that.
|
||||
|
|
@ -42,11 +42,11 @@ signal, you can use the :meth:`~blinker.base.Signal.disconnect` method.
|
|||
|
||||
For all core Flask signals, the sender is the application that issued the
|
||||
signal. When you subscribe to a signal, be sure to also provide a sender
|
||||
unless you really want to listen for signals of all applications. This is
|
||||
unless you really want to listen for signals from all applications. This is
|
||||
especially true if you are developing an extension.
|
||||
|
||||
Here for example a helper context manager that can be used to figure out
|
||||
in a unittest which templates were rendered and what variables were passed
|
||||
For example, here is a helper context manager that can be used in a unittest
|
||||
to determine which templates were rendered and what variables were passed
|
||||
to the template::
|
||||
|
||||
from flask import template_rendered
|
||||
|
|
@ -77,15 +77,15 @@ Make sure to subscribe with an extra ``**extra`` argument so that your
|
|||
calls don't fail if Flask introduces new arguments to the signals.
|
||||
|
||||
All the template rendering in the code issued by the application `app`
|
||||
in the body of the `with` block will now be recorded in the `templates`
|
||||
in the body of the ``with`` block will now be recorded in the `templates`
|
||||
variable. Whenever a template is rendered, the template object as well as
|
||||
context are appended to it.
|
||||
|
||||
Additionally there is a convenient helper method
|
||||
(:meth:`~blinker.base.Signal.connected_to`). that allows you to
|
||||
(:meth:`~blinker.base.Signal.connected_to`) that allows you to
|
||||
temporarily subscribe a function to a signal with a context manager on
|
||||
its own. Because the return value of the context manager cannot be
|
||||
specified that way one has to pass the list in as argument::
|
||||
specified that way, you have to pass the list in as an argument::
|
||||
|
||||
from flask import template_rendered
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ signal subscribers::
|
|||
model_saved.send(self)
|
||||
|
||||
Try to always pick a good sender. If you have a class that is emitting a
|
||||
signal, pass `self` as sender. If you emitting a signal from a random
|
||||
signal, pass ``self`` as sender. If you are emitting a signal from a random
|
||||
function, you can pass ``current_app._get_current_object()`` as sender.
|
||||
|
||||
.. admonition:: Passing Proxies as Senders
|
||||
|
|
@ -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
|
||||
|
||||
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 before any request processing started but when the
|
||||
request context was set up. 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
|
||||
Take a look at :ref:`core-signals-list` for a list of all builtin signals.
|
||||
|
||||
|
||||
.. 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: http://pypi.python.org/pypi/blinker
|
||||
.. _blinker: https://pypi.python.org/pypi/blinker
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ Comparisons:
|
|||
- against arbitrary types: ``==`` and ``!=``
|
||||
- against singletons with ``is`` and ``is not`` (eg: ``foo is not
|
||||
None``)
|
||||
- never compare something with `True` or `False` (for example never
|
||||
- never compare something with ``True`` or ``False`` (for example never
|
||||
do ``foo == False``, do ``not foo`` instead)
|
||||
|
||||
Negated containment checks:
|
||||
|
|
@ -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::
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ An extension can depend on Jinja2 being present.
|
|||
This section only gives a very quick introduction into how Jinja2
|
||||
is integrated into Flask. If you want information on the template
|
||||
engine's syntax itself, head over to the official `Jinja2 Template
|
||||
Documentation <http://jinja.pocoo.org/2/documentation/templates>`_ for
|
||||
Documentation <http://jinja.pocoo.org/docs/templates>`_ for
|
||||
more information.
|
||||
|
||||
Jinja Setup
|
||||
|
|
@ -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
|
||||
|
|
@ -105,9 +108,9 @@ by Jinja2 itself:
|
|||
is for example very helpful if you try to generate JavaScript on the
|
||||
fly.
|
||||
|
||||
Note that inside `script` tags no escaping must take place, so make
|
||||
Note that inside ``script`` tags no escaping must take place, so make
|
||||
sure to disable escaping with ``|safe`` before Flask 0.10 if you intend
|
||||
to use it inside `script` tags:
|
||||
to use it inside ``script`` tags:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -119,7 +122,7 @@ Controlling Autoescaping
|
|||
------------------------
|
||||
|
||||
Autoescaping is the concept of automatically escaping special characters
|
||||
of you. Special characters in the sense of HTML (or XML, and thus XHTML)
|
||||
for you. Special characters in the sense of HTML (or XML, and thus XHTML)
|
||||
are ``&``, ``>``, ``<``, ``"`` as well as ``'``. Because these characters
|
||||
carry specific meanings in documents on their own you have to replace them
|
||||
by so called "entities" if you want to use them for text. Not doing so
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ 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
|
||||
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.
|
||||
|
|
@ -19,21 +19,21 @@ we will use the :mod:`unittest` package that comes pre-installed with Python.
|
|||
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
|
||||
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`_.
|
||||
|
||||
.. _the examples:
|
||||
http://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
|
||||
(`flaskr_tests.py`) and create a unittest skeleton there::
|
||||
In order to test the application, we add a second module
|
||||
(:file:`flaskr_tests.py`) and create a unittest skeleton there::
|
||||
|
||||
import os
|
||||
import flaskr
|
||||
from flaskr import flaskr
|
||||
import unittest
|
||||
import tempfile
|
||||
|
||||
|
|
@ -41,9 +41,10 @@ In order to test the application, we add a second module
|
|||
|
||||
def setUp(self):
|
||||
self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
|
||||
flaskr.app.config['TESTING'] = True
|
||||
flaskr.app.testing = True
|
||||
self.app = flaskr.app.test_client()
|
||||
flaskr.init_db()
|
||||
with flaskr.app.app_context():
|
||||
flaskr.init_db()
|
||||
|
||||
def tearDown(self):
|
||||
os.close(self.db_fd)
|
||||
|
|
@ -54,15 +55,15 @@ In order to test the application, we add a second module
|
|||
|
||||
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
|
||||
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 disabling the error
|
||||
``TESTING`` config flag is activated. What it does is disable the error
|
||||
catching during request handling so that you get better error reports when
|
||||
performing test requests against the application.
|
||||
|
||||
This 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
|
||||
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.
|
||||
|
||||
Because SQLite3 is filesystem-based we can easily use the tempfile module
|
||||
|
|
@ -88,8 +89,8 @@ with an exception.
|
|||
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
|
||||
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::
|
||||
|
||||
|
|
@ -97,8 +98,10 @@ test method to our class, like this::
|
|||
|
||||
def setUp(self):
|
||||
self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
|
||||
flaskr.app.testing = True
|
||||
self.app = flaskr.app.test_client()
|
||||
flaskr.init_db()
|
||||
with flaskr.app.app_context():
|
||||
flaskr.init_db()
|
||||
|
||||
def tearDown(self):
|
||||
os.close(self.db_fd)
|
||||
|
|
@ -106,15 +109,15 @@ test method to our class, like this::
|
|||
|
||||
def test_empty_db(self):
|
||||
rv = self.app.get('/')
|
||||
assert 'No entries here so far' in rv.data
|
||||
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.
|
||||
Notice that our test functions begin with the word `test`; this allows
|
||||
:mod:`unittest` to automatically identify the method as a test to run.
|
||||
|
||||
By using `self.app.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.
|
||||
By using `self.app.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
|
||||
the return value (as string) from the application. In this case, we ensure that
|
||||
``'No entries here so far'`` is part of the output.
|
||||
|
||||
Run it again and you should see one passing test::
|
||||
|
|
@ -131,8 +134,8 @@ Logging In and Out
|
|||
|
||||
The majority of the functionality of our application is only available for
|
||||
the administrative user, so we need a way to log our test client in and out
|
||||
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
|
||||
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::
|
||||
|
|
@ -151,13 +154,13 @@ invalid credentials. Add this new test to the class::
|
|||
|
||||
def test_login_logout(self):
|
||||
rv = self.login('admin', 'default')
|
||||
assert 'You were logged in' in rv.data
|
||||
assert b'You were logged in' in rv.data
|
||||
rv = self.logout()
|
||||
assert 'You were logged out' in rv.data
|
||||
assert b'You were logged out' in rv.data
|
||||
rv = self.login('adminx', 'default')
|
||||
assert 'Invalid username' in rv.data
|
||||
assert b'Invalid username' in rv.data
|
||||
rv = self.login('admin', 'defaultx')
|
||||
assert 'Invalid password' in rv.data
|
||||
assert b'Invalid password' in rv.data
|
||||
|
||||
Test Adding Messages
|
||||
--------------------
|
||||
|
|
@ -171,9 +174,9 @@ like this::
|
|||
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.
|
||||
|
|
@ -193,7 +196,7 @@ suite.
|
|||
|
||||
|
||||
.. _MiniTwit Example:
|
||||
http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/
|
||||
https://github.com/pallets/flask/tree/master/examples/minitwit/
|
||||
|
||||
|
||||
Other Testing Tricks
|
||||
|
|
@ -201,11 +204,13 @@ Other Testing Tricks
|
|||
|
||||
Besides using the test client as shown above, there is also the
|
||||
:meth:`~flask.Flask.test_request_context` method that can be used
|
||||
in combination with the `with` statement to activate a request context
|
||||
in combination with the ``with`` statement to activate a request context
|
||||
temporarily. With this you can access the :class:`~flask.request`,
|
||||
:class:`~flask.g` and :class:`~flask.session` objects like in view
|
||||
functions. Here is a full example that demonstrates this approach::
|
||||
|
||||
import flask
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
with app.test_request_context('/?name=Peter'):
|
||||
|
|
@ -220,10 +225,10 @@ 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
|
||||
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
|
||||
need to call :meth:`~flask.Flask.preprocess_request` yourself::
|
||||
|
||||
|
|
@ -271,11 +276,11 @@ 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
|
||||
from flask import appcontext_pushed
|
||||
from flask import appcontext_pushed, g
|
||||
|
||||
@contextmanager
|
||||
def user_set(app, user):
|
||||
|
|
@ -307,7 +312,7 @@ Keeping the Context Around
|
|||
Sometimes it is helpful to trigger a regular request but still keep the
|
||||
context around for a little longer so that additional introspection can
|
||||
happen. With Flask 0.4 this is possible by using the
|
||||
:meth:`~flask.Flask.test_client` with a `with` block::
|
||||
:meth:`~flask.Flask.test_client` with a ``with`` block::
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
|
|
@ -316,7 +321,7 @@ happen. With Flask 0.4 this is possible by using the
|
|||
assert request.args['tequila'] == '42'
|
||||
|
||||
If you were to use just the :meth:`~flask.Flask.test_client` without
|
||||
the `with` block, the `assert` would fail with an error because `request`
|
||||
the ``with`` block, the ``assert`` would fail with an error because `request`
|
||||
is no longer available (because you are trying to use it outside of the actual request).
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 `style.css` in the `static`
|
||||
folder we created before:
|
||||
application. Just create a stylesheet called :file:`style.css` in the
|
||||
:file:`static` folder:
|
||||
|
||||
.. sourcecode:: css
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,30 @@
|
|||
.. _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. So how can we elegantly do that with Flask?
|
||||
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*.
|
||||
|
||||
This is where the application context comes into play. So let's start
|
||||
there.
|
||||
|
||||
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
|
||||
are special variables that use these. For instance the
|
||||
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
|
||||
For the time being, all you have to know is that you can store information
|
||||
safely on the :data:`~flask.g` object.
|
||||
|
||||
So when do you put it on there? To do that you can make a helper
|
||||
function. The first time the function is called it will create a database
|
||||
connection for the current context and successive calls will return the
|
||||
function. The first time the function is called, it will create a database
|
||||
connection for the current context, and successive calls will return the
|
||||
already established connection::
|
||||
|
||||
def get_db():
|
||||
|
|
@ -37,9 +35,8 @@ 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
|
||||
that flask provides us with the :meth:`~flask.Flask.teardown_appcontext`
|
||||
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::
|
||||
|
||||
@app.teardown_appcontext
|
||||
|
|
@ -49,11 +46,11 @@ decorator. It's executed every time the application context tears down::
|
|||
g.sqlite_db.close()
|
||||
|
||||
Functions marked with :meth:`~flask.Flask.teardown_appcontext` are called
|
||||
every time the app context tears down. So what does this mean?
|
||||
Essentially the app context is created before the request comes in and is
|
||||
destroyed (teared down) whenever the request finishes. A teardown can
|
||||
every time the app context tears down. What does this mean?
|
||||
Essentially, the app context is created before the request comes in and is
|
||||
destroyed (torn down) whenever the request finishes. A teardown can
|
||||
happen because of two reasons: either everything went well (the error
|
||||
parameter will be `None`) or an exception happend in which case the error
|
||||
parameter will be ``None``) or an exception happened, in which case the error
|
||||
is passed to the teardown function.
|
||||
|
||||
Curious about what these contexts mean? Have a look at the
|
||||
|
|
@ -75,4 +72,4 @@ Continue to :ref:`tutorial-dbinit`.
|
|||
larger <larger-applications>`, it's a good idea not to.
|
||||
|
||||
.. _example source:
|
||||
http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
|
||||
https://github.com/pallets/flask/tree/master/examples/flaskr/
|
||||
|
|
|
|||
|
|
@ -1,69 +1,73 @@
|
|||
.. _tutorial-dbinit:
|
||||
|
||||
Step 4: Creating The Database
|
||||
Step 5: Creating The Database
|
||||
=============================
|
||||
|
||||
As outlined earlier, Flaskr is a database powered application, and more
|
||||
precisely, it is an application powered by a relational database system. Such
|
||||
systems need a schema that tells them how to store that information. So
|
||||
before starting the server for the first time it's important to create
|
||||
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
|
||||
Such a schema can 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
|
||||
require that we provide the path to the database which can introduce
|
||||
The downside of this is that it requires the ``sqlite3`` command to be
|
||||
installed, which is not necessarily the case on every system. This also
|
||||
requires that you provide the path to the database, which can introduce
|
||||
errors. It's a good idea to add a function that initializes the database
|
||||
for you to the application.
|
||||
for you, to the application.
|
||||
|
||||
To do this we can create a function called `init_db` that initializes the
|
||||
database. Let me show you the code first. Just add this function below
|
||||
the `connect_db` function in `flaskr.py`::
|
||||
To do this, you can create a function and hook it into a :command:`flask`
|
||||
command that initializes the database. For now just take a look at the
|
||||
code segment below. A good place to add this function, and command, is
|
||||
just below the `connect_db` function in :file:`flaskr.py`::
|
||||
|
||||
def init_db():
|
||||
with app.app_context():
|
||||
db = get_db()
|
||||
with app.open_resource('schema.sql', mode='r') as f:
|
||||
db.cursor().executescript(f.read())
|
||||
db.commit()
|
||||
db = get_db()
|
||||
with app.open_resource('schema.sql', mode='r') as f:
|
||||
db.cursor().executescript(f.read())
|
||||
db.commit()
|
||||
|
||||
So what's happening here? Remember how we learned last chapter that the
|
||||
application context is created every time a request comes in? Here we
|
||||
don't have a request yet, so we need to create the application context by
|
||||
hand. Without an application context the :data:`~flask.g` object does not
|
||||
know yet to which application it becomes as there could be more than one!
|
||||
@app.cli.command('initdb')
|
||||
def initdb_command():
|
||||
"""Initializes the database."""
|
||||
init_db()
|
||||
print('Initialized the database.')
|
||||
|
||||
The ``with app.app_context()`` statement establishes the application
|
||||
context for us. In the body of the with statement the :data:`~flask.g`
|
||||
object will be associated with ``app``. At the end of the with statement
|
||||
the association is released and all teardown functions are executed. This
|
||||
means that our database connection is disconnected after the commit.
|
||||
The ``app.cli.command()`` decorator registers a new command with the
|
||||
:command:`flask` script. When the command executes, Flask will automatically
|
||||
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.
|
||||
|
||||
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
|
||||
only have to commit the changes. SQLite 3 and other transactional
|
||||
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 by starting up a Python shell and
|
||||
importing and calling that function::
|
||||
Now, it is possible to create a database with the :command:`flask` script::
|
||||
|
||||
>>> from flaskr import init_db
|
||||
>>> init_db()
|
||||
flask initdb
|
||||
Initialized the database.
|
||||
|
||||
.. admonition:: Troubleshooting
|
||||
|
||||
If you get an exception later that a table cannot be found check that
|
||||
you did call the `init_db` function and that your table names are
|
||||
correct (singular vs. plural for example).
|
||||
If you get an exception later on stating that a table cannot be found, check
|
||||
that you did execute the ``initdb`` command and that your table names are
|
||||
correct (singular vs. plural, for example).
|
||||
|
||||
Continue with :ref:`tutorial-views`
|
||||
|
|
|
|||
|
|
@ -3,21 +3,25 @@
|
|||
Step 0: Creating The Folders
|
||||
============================
|
||||
|
||||
Before we get started, let's create the folders needed for this
|
||||
Before getting started, you will need to create the folders needed for this
|
||||
application::
|
||||
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
|
||||
The `flaskr` folder is not a python package, but just something where we
|
||||
drop our files. We will then put our database schema as well as main module
|
||||
into this folder. It is done in the following way. The files inside
|
||||
the `static` folder are available to users of the application via `HTTP`.
|
||||
This is the place where css and javascript files go. Inside the
|
||||
`templates` folder Flask will look for `Jinja2`_ templates. The
|
||||
templates you create later 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. 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.
|
||||
|
||||
Continue with :ref:`tutorial-schema`.
|
||||
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.
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/2/
|
||||
For now you should continue with :ref:`tutorial-schema`.
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ Tutorial
|
|||
========
|
||||
|
||||
You want to develop an application with Python and Flask? Here you have
|
||||
the chance to learn that by example. In this tutorial we will create a
|
||||
simple microblog application. It only supports one user that can create
|
||||
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 database which comes out of the box with Python, so there is nothing
|
||||
as a database (which comes out of the box with Python) so there is nothing
|
||||
else you need.
|
||||
|
||||
If you want the full sourcecode in advance or for comparison, check out
|
||||
If you want the full source code in advance or for comparison, check out
|
||||
the `example source`_.
|
||||
|
||||
.. _example source:
|
||||
http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
|
||||
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,25 +3,26 @@
|
|||
Introducing Flaskr
|
||||
==================
|
||||
|
||||
We will call our blogging application flaskr here, feel free to choose a
|
||||
less web-2.0-ish name ;) Basically 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
|
||||
1. Let the user sign in and out with credentials specified in the
|
||||
configuration. Only one user is supported.
|
||||
2. when the user is logged in they can add new entries to the page
|
||||
2. When the user is logged in, they can add new entries to the page
|
||||
consisting of a text-only title and some HTML for the text. This HTML
|
||||
is not sanitized because we trust the user here.
|
||||
3. the page shows all entries so far in reverse order (newest on top) and
|
||||
the user can add new ones from there if logged in.
|
||||
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 that application because it's good
|
||||
enough for an application of that size. For larger applications however
|
||||
it makes a lot of sense to use `SQLAlchemy`_ that handles database
|
||||
connections in a more intelligent way, allows you to target different
|
||||
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 from the final application:
|
||||
Here a screenshot of the final application:
|
||||
|
||||
.. image:: ../_static/flaskr.png
|
||||
:align: center
|
||||
|
|
@ -30,4 +31,4 @@ Here a screenshot from the final application:
|
|||
|
||||
Continue with :ref:`tutorial-folders`.
|
||||
|
||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||
|
|
|
|||
108
docs/tutorial/packaging.rst
Normal file
108
docs/tutorial/packaging.rst
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
.. _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
|
||||
|
||||
The content of the ``setup.py`` file for ``flaskr`` is:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
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
|
||||
|
||||
To simplify locating the application, add the following import statement
|
||||
into this file, :file:`flaskr/__init__.py`:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
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 it the export
|
||||
statement a few steps below would need to be
|
||||
``export FLASK_APP=flaskr.flaskr``.
|
||||
|
||||
At this point you should be able to install the application. As usual, it
|
||||
is recommended to install your Flask application within a `virtualenv`_.
|
||||
With that said, go ahead and install the application with::
|
||||
|
||||
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 with the following commands::
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -14,11 +14,11 @@ named `schema.sql` in the just created `flaskr` folder:
|
|||
create table entries (
|
||||
id integer primary key autoincrement,
|
||||
title text not null,
|
||||
text text not null
|
||||
'text' text not null
|
||||
);
|
||||
|
||||
This schema consists of a single table called `entries` and each row in
|
||||
this table has an `id`, a `title` and a `text`. The `id` is an
|
||||
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,15 +3,16 @@
|
|||
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 flask 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 and
|
||||
load that or import the values from there.
|
||||
Now that the schema is in place, you can create the application module,
|
||||
:file:`flaskr.py`. This file should be placed inside of the
|
||||
:file:`flaskr/flaskr` folder. The first several lines of code in the
|
||||
application module are the needed import statements. After that there will be a
|
||||
few lines of configuration code. For small applications like ``flaskr``, it is
|
||||
possible to drop the configuration directly into the module. However, a cleaner
|
||||
solution is to create a separate ``.ini`` or ``.py`` file, load that, and
|
||||
import the values from there.
|
||||
|
||||
First we add the imports in `flaskr.py`::
|
||||
Here are the import statements (in :file:`flaskr.py`)::
|
||||
|
||||
# all the imports
|
||||
import os
|
||||
|
|
@ -19,72 +20,71 @@ First we add the imports in `flaskr.py`::
|
|||
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 `flaskr.py`::
|
||||
The next couple lines will create the actual application instance and
|
||||
initialize it with the config from the same file in :file:`flaskr.py`:
|
||||
|
||||
# create our little application :)
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(__name__)
|
||||
.. sourcecode:: python
|
||||
|
||||
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(
|
||||
DATABASE=os.path.join(app.root_path, 'flaskr.db'),
|
||||
DEBUG=True,
|
||||
SECRET_KEY='development key',
|
||||
USERNAME='admin',
|
||||
PASSWORD='default'
|
||||
))
|
||||
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
|
||||
|
||||
The :class:`~flask.Config` object works similar to a dictionary so we
|
||||
can update it with new values.
|
||||
The :class:`~flask.Config` object works similarly to a dictionary, so it can be
|
||||
updated with new values.
|
||||
|
||||
.. admonition:: Database Path
|
||||
|
||||
Operating systems know the concept of a current working directory for
|
||||
each process. Unfortunately you cannot depend on this in web
|
||||
each process. Unfortunately, you cannot depend on this in web
|
||||
applications because you might have more than one application in the
|
||||
same process.
|
||||
|
||||
For this reason the ``app.root_path`` attribute can be used to
|
||||
get the path to the application. Together with the ``os.path`` module
|
||||
files can then easily be found. In this example we place the
|
||||
get the path to the application. Together with the ``os.path`` module,
|
||||
files can then easily be found. In this example, we place the
|
||||
database right next to it.
|
||||
|
||||
For a real-work application it's recommended to use
|
||||
For a real-world application, it's recommended to use
|
||||
:ref:`instance-folders` instead.
|
||||
|
||||
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.
|
||||
|
||||
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
|
||||
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.
|
||||
|
||||
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
|
||||
.. sourcecode:: python
|
||||
|
||||
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.
|
||||
|
||||
In addition to that you can use the :meth:`~flask.Config.from_object`
|
||||
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
|
||||
module. Flask will the initialize the variable from that module. Note
|
||||
that in all cases only variable names that are uppercase are considered.
|
||||
module. Flask will then initialize the variable from that module. Note
|
||||
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. The
|
||||
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!
|
||||
Choose that key wisely and as hard to guess and complex as possible.
|
||||
|
||||
We will also add a method that allows for easily connecting to the
|
||||
Lastly, you will add a method that allows for easy connections to the
|
||||
specified database. This can be used to open a connection on request and
|
||||
also from the interactive Python shell or a script. This will come in
|
||||
handy later. We create a simple database connection through SQLite and
|
||||
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 us to treat the rows as if they were dictionaries instead of
|
||||
This allows the rows to be treated as if they were dictionaries instead of
|
||||
tuples.
|
||||
|
||||
::
|
||||
.. sourcecode:: python
|
||||
|
||||
def connect_db():
|
||||
"""Connects to the specific database."""
|
||||
|
|
@ -92,28 +92,6 @@ tuples.
|
|||
rv.row_factory = sqlite3.Row
|
||||
return rv
|
||||
|
||||
Finally we just add a line to the bottom of the file that fires up the
|
||||
server if we want to run that file as a standalone application::
|
||||
In the next section you will see how to run the application.
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
With that out of the way you should be able to start up the application
|
||||
without problems. Do this with the following command::
|
||||
|
||||
python flaskr.py
|
||||
|
||||
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 an 404
|
||||
page not found error because we don't have any views yet. But we will
|
||||
focus on that a little later. 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 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,9 +15,9 @@ 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 `templates` folder:
|
||||
Put the following templates into the :file:`templates` folder:
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/2/documentation/templates
|
||||
.. _Jinja2: http://jinja.pocoo.org/docs/templates
|
||||
|
||||
layout.html
|
||||
-----------
|
||||
|
|
@ -55,11 +56,11 @@ the session:
|
|||
show_entries.html
|
||||
-----------------
|
||||
|
||||
This template extends the `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:
|
||||
This template extends the :file:`layout.html` template from above to display the
|
||||
messages. Note that the ``for`` loop iterates over the messages we passed
|
||||
in with the :func:`~flask.render_template` function. Notice that the form is
|
||||
configured to submit to the `add_entry` view function and use ``POST`` as
|
||||
HTTP method:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -78,9 +79,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 %}
|
||||
|
|
@ -88,7 +89,7 @@ method:
|
|||
login.html
|
||||
----------
|
||||
|
||||
Finally the login template which basically just displays a form to allow
|
||||
This is the login template, which basically just displays a form to allow
|
||||
the user to login:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
|
|
|||
|
|
@ -6,5 +6,91 @@ Bonus: Testing the Application
|
|||
Now that you have finished the application and everything works as
|
||||
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 unittesting in the :ref:`testing` section of the
|
||||
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,10 @@
|
|||
.. _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
|
||||
------------
|
||||
|
|
@ -12,11 +12,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 dicts to the
|
||||
`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():
|
||||
|
|
@ -29,8 +29,8 @@ 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
|
||||
responds to ``POST`` requests; the actual form is shown on the
|
||||
`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 +45,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
|
||||
|
||||
|
|
@ -60,8 +60,8 @@ Login and Logout
|
|||
|
||||
These functions are used to sign the user in and out. Login checks the
|
||||
username and password against the ones from the configuration and sets the
|
||||
`logged_in` key in the session. If the user logged in successfully, that
|
||||
key is set to `True`, and the user is redirected back to the `show_entries`
|
||||
`logged_in` key for the session. If the user logged in successfully, that
|
||||
key is set to ``True``, and the user is redirected back to the `show_entries`
|
||||
page. In addition, a message is flashed that informs the user that he or
|
||||
she was logged in successfully. If an error occurred, the template is
|
||||
notified about that, and the user is asked again::
|
||||
|
|
@ -80,12 +80,12 @@ notified about that, and the user is asked again::
|
|||
return redirect(url_for('show_entries'))
|
||||
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
|
||||
The `logout` function, on the other hand, removes that key from the session
|
||||
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 +95,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,10 +14,110 @@ 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 `easy_install` command to upgrade your Flask
|
||||
installation, make sure to pass it the ``-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-012:
|
||||
|
||||
Version 0.12
|
||||
------------
|
||||
|
||||
Changes to send_file
|
||||
````````````````````
|
||||
|
||||
The ``filename`` is no longer automatically inferred from file-like objects.
|
||||
This means that the following code will no longer automatically have
|
||||
``X-Sendfile`` support, etag generation or MIME-type guessing::
|
||||
|
||||
response = send_file(open('/path/to/file.txt'))
|
||||
|
||||
Any of the following is functionally equivalent::
|
||||
|
||||
fname = '/path/to/file.txt'
|
||||
|
||||
# Just pass the filepath directly
|
||||
response = send_file(fname)
|
||||
|
||||
# Set the MIME-type and ETag explicitly
|
||||
response = send_file(open(fname), mimetype='text/plain')
|
||||
response.set_etag(...)
|
||||
|
||||
# Set `attachment_filename` for MIME-type guessing
|
||||
# ETag still needs to be manually set
|
||||
response = send_file(open(fname), attachment_filename=fname)
|
||||
response.set_etag(...)
|
||||
|
||||
The reason for this is that some file-like objects have an invalid or even
|
||||
misleading ``name`` attribute. Silently swallowing errors in such cases was not
|
||||
a satisfying solution.
|
||||
|
||||
Additionally the default of falling back to ``application/octet-stream`` has
|
||||
been restricted. If Flask can't guess one or the user didn't provide one, the
|
||||
function fails if no filename information was provided.
|
||||
|
||||
.. _upgrading-to-011:
|
||||
|
||||
Version 0.11
|
||||
------------
|
||||
|
||||
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
|
||||
:meth:`~flask.Flask.register_error_handler`, respectively.
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
|
|
@ -43,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://packages.python.org/Flask-OldSessions/
|
||||
.. _Flask-OldSessions: https://pythonhosted.org/Flask-OldSessions/
|
||||
|
||||
Version 0.9
|
||||
-----------
|
||||
|
|
@ -64,14 +164,14 @@ If you maintain an extension that was using :data:`~flask._request_ctx_stack`
|
|||
before, please consider changing to :data:`~flask._app_ctx_stack` if it makes
|
||||
sense for your extension. For instance, the app context stack makes sense for
|
||||
extensions which connect to databases. Using the app context stack instead of
|
||||
the request context stack will make extensions more readily handle use cases
|
||||
the request context stack will make extensions more readily handle use cases
|
||||
outside of requests.
|
||||
|
||||
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
|
||||
|
|
@ -82,11 +182,11 @@ If invalid JSON data was submitted Flask will now raise a
|
|||
default :exc:`ValueError` bubble up. This has the advantage that you no
|
||||
longer have to handle that error to avoid an internal server error showing
|
||||
up for the user. If you were catching this down explicitly in the past
|
||||
as `ValueError` you will need to change this.
|
||||
as :exc:`ValueError` you will need to change this.
|
||||
|
||||
Due to a bug in the test client Flask 0.7 did not trigger teardown
|
||||
handlers when the test client was used in a with statement. This was
|
||||
since fixed but might require some changes in your testsuites if you
|
||||
since fixed but might require some changes in your test suites if you
|
||||
relied on this behavior.
|
||||
|
||||
Version 0.7
|
||||
|
|
@ -98,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
|
||||
|
|
@ -115,7 +215,7 @@ good.
|
|||
To apply the upgrade script do the following:
|
||||
|
||||
1. Download the script: `flask-07-upgrade.py
|
||||
<https://raw.github.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
|
||||
|
|
@ -126,18 +226,18 @@ To apply the upgrade script do the following:
|
|||
patch -p1 < patchfile.diff
|
||||
|
||||
5. If you were using per-module template folders you need to move some
|
||||
templates around. Previously if you had a folder named ``templates``
|
||||
templates around. Previously if you had a folder named :file:`templates`
|
||||
next to a blueprint named ``admin`` the implicit template path
|
||||
automatically was ``admin/index.html`` for a template file called
|
||||
``templates/index.html``. This no longer is the case. Now you need
|
||||
to name the template ``templates/admin/index.html``. The tool will
|
||||
automatically was :file:`admin/index.html` for a template file called
|
||||
:file:`templates/index.html`. This no longer is the case. Now you need
|
||||
to name the template :file:`templates/admin/index.html`. The tool will
|
||||
not detect this so you will have to do that on your own.
|
||||
|
||||
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
|
||||
|
|
@ -154,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
|
||||
|
|
@ -248,20 +348,20 @@ applications automatically, but there might be some cases where it fails
|
|||
to upgrade. What changed?
|
||||
|
||||
- Blueprints need explicit names. Modules had an automatic name
|
||||
guesssing scheme where the shortname for the module was taken from the
|
||||
guessing scheme where the shortname for the module was taken from the
|
||||
last part of the import module. The upgrade script tries to guess
|
||||
that name but it might fail as this information could change at
|
||||
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
|
||||
modules.
|
||||
- Blueprints do not automatically provide static folders. They will
|
||||
also no longer automatically export templates from a folder called
|
||||
`templates` next to their location however but it can be enabled from
|
||||
:file:`templates` next to their location however but it can be enabled from
|
||||
the constructor. Same with static files: if you want to continue
|
||||
serving static files you need to tell the constructor explicitly the
|
||||
path to the static folder (which can be relative to the blueprint's
|
||||
|
|
@ -269,10 +369,10 @@ to upgrade. What changed?
|
|||
- Rendering templates was simplified. Now the blueprints can provide
|
||||
template folders which are added to a general template searchpath.
|
||||
This means that you need to add another subfolder with the blueprint's
|
||||
name into that folder if you want ``blueprintname/template.html`` as
|
||||
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,
|
||||
|
|
@ -286,13 +386,13 @@ Flask 0.6 comes with a backwards incompatible change which affects the
|
|||
order of after-request handlers. Previously they were called in the order
|
||||
of the registration, now they are called in reverse order. This change
|
||||
was made so that Flask behaves more like people expected it to work and
|
||||
how other systems handle request pre- and postprocessing. If you
|
||||
how other systems handle request pre- and post-processing. If you
|
||||
depend on the order of execution of post-request functions, be sure to
|
||||
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
|
||||
|
|
@ -318,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.
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class into an actual view function by using the
|
|||
that function is the name of the endpoint that view will then have. But
|
||||
this by itself is not helpful, so let's refactor the code a bit::
|
||||
|
||||
|
||||
|
||||
from flask.views import View
|
||||
|
||||
class ListView(View):
|
||||
|
|
@ -71,7 +71,7 @@ this by itself is not helpful, so let's refactor the code a bit::
|
|||
|
||||
This of course is not that helpful for such a small example, but it's good
|
||||
enough to explain the basic principle. When you have a class-based view
|
||||
the question comes up what `self` points to. The way this works is that
|
||||
the question comes up what ``self`` points to. The way this works is that
|
||||
whenever the request is dispatched a new instance of the class is created
|
||||
and the :meth:`~flask.views.View.dispatch_request` method is called with
|
||||
the parameters from the URL rule. The class itself is instantiated with
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ from simple_page.simple_page import simple_page
|
|||
app = Flask(__name__)
|
||||
app.register_blueprint(simple_page)
|
||||
# Blueprint can be registered many times
|
||||
app.register_blueprint(simple_page, url_prefix='/pages')
|
||||
app.register_blueprint(simple_page, url_prefix='/pages')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
if __name__=='__main__':
|
||||
app.run()
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Blueprint Example Tests
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Tests the Blueprint example app
|
||||
"""
|
||||
import blueprintexample
|
||||
import unittest
|
||||
|
||||
|
||||
class BlueprintExampleTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.app = blueprintexample.app.test_client()
|
||||
|
||||
def test_urls(self):
|
||||
r = self.app.get('/')
|
||||
self.assertEquals(r.status_code, 200)
|
||||
|
||||
r = self.app.get('/hello')
|
||||
self.assertEquals(r.status_code, 200)
|
||||
|
||||
r = self.app.get('/world')
|
||||
self.assertEquals(r.status_code, 200)
|
||||
|
||||
#second blueprint instance
|
||||
r = self.app.get('/pages/hello')
|
||||
self.assertEquals(r.status_code, 200)
|
||||
|
||||
r = self.app.get('/pages/world')
|
||||
self.assertEquals(r.status_code, 200)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
<!doctype html>
|
||||
<title>Simple Page Blueprint</title>
|
||||
<div class=page>
|
||||
<div class="page">
|
||||
<h1>This is blueprint example</h1>
|
||||
<p>
|
||||
A simple page blueprint is registered under / and /pages
|
||||
you can access it using this urls:
|
||||
you can access it using this URLs:
|
||||
<ul>
|
||||
<li><a href="{{ url_for('simple_page.show', page='hello') }}">/hello</a>
|
||||
<li><a href="{{ url_for('simple_page.show', page='world') }}">/world</a>
|
||||
|
|
|
|||
33
examples/blueprintexample/test_blueprintexample.py
Normal file
33
examples/blueprintexample/test_blueprintexample.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Blueprint Example Tests
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Tests the Blueprint example app
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import blueprintexample
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
return blueprintexample.app.test_client()
|
||||
|
||||
|
||||
def test_urls(client):
|
||||
r = client.get('/')
|
||||
assert r.status_code == 200
|
||||
|
||||
r = client.get('/hello')
|
||||
assert r.status_code == 200
|
||||
|
||||
r = client.get('/world')
|
||||
assert r.status_code == 200
|
||||
|
||||
# second blueprint instance
|
||||
r = client.get('/pages/hello')
|
||||
assert r.status_code == 200
|
||||
|
||||
r = client.get('/pages/world')
|
||||
assert r.status_code == 200
|
||||
2
examples/flaskr/.gitignore
vendored
Normal file
2
examples/flaskr/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
flaskr.db
|
||||
.eggs/
|
||||
3
examples/flaskr/MANIFEST.in
Normal file
3
examples/flaskr/MANIFEST.in
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
graft flaskr/templates
|
||||
graft flaskr/static
|
||||
include flaskr/schema.sql
|
||||
|
|
@ -13,11 +13,26 @@
|
|||
export an FLASKR_SETTINGS environment variable
|
||||
pointing to a configuration file.
|
||||
|
||||
2. now you can run the flaskr.py file with your
|
||||
python interpreter and the application will
|
||||
greet you on http://localhost:5000/
|
||||
|
||||
2. install the app from the root of the project directory
|
||||
|
||||
pip install --editable .
|
||||
|
||||
3. Instruct flask to use the right application
|
||||
|
||||
export FLASK_APP=flaskr
|
||||
|
||||
4. initialize the database with this command:
|
||||
|
||||
flask initdb
|
||||
|
||||
5. now you can run flaskr:
|
||||
|
||||
flask run
|
||||
|
||||
the application will greet you on
|
||||
http://localhost:5000/
|
||||
|
||||
~ Is it tested?
|
||||
|
||||
You betcha. Run the `flaskr_tests.py` file to see
|
||||
You betcha. Run `python setup.py test` to see
|
||||
the tests pass.
|
||||
|
|
|
|||
1
examples/flaskr/flaskr/__init__.py
Normal file
1
examples/flaskr/flaskr/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .flaskr import app
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
A microblog example application written as Flask tutorial with
|
||||
Flask and sqlite3.
|
||||
|
||||
:copyright: (c) 2014 by Armin Ronacher.
|
||||
:copyright: (c) 2015 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
|
@ -38,12 +38,18 @@ def connect_db():
|
|||
|
||||
|
||||
def init_db():
|
||||
"""Initializes the database."""
|
||||
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():
|
||||
"""Creates the database tables."""
|
||||
with app.app_context():
|
||||
db = get_db()
|
||||
with app.open_resource('schema.sql', mode='r') as f:
|
||||
db.cursor().executescript(f.read())
|
||||
db.commit()
|
||||
init_db()
|
||||
print('Initialized the database.')
|
||||
|
||||
|
||||
def get_db():
|
||||
|
|
@ -76,7 +82,7 @@ def add_entry():
|
|||
abort(401)
|
||||
db = get_db()
|
||||
db.execute('insert into entries (title, text) values (?, ?)',
|
||||
[request.form['title'], request.form['text']])
|
||||
[request.form['title'], request.form['text']])
|
||||
db.commit()
|
||||
flash('New entry was successfully posted')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
|
@ -102,8 +108,3 @@ def logout():
|
|||
session.pop('logged_in', None)
|
||||
flash('You were logged out')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_db()
|
||||
app.run()
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<!doctype html>
|
||||
<title>Flaskr</title>
|
||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
|
||||
<div class=page>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
|
||||
<div class="page">
|
||||
<h1>Flaskr</h1>
|
||||
<div class=metanav>
|
||||
<div class="metanav">
|
||||
{% if not session.logged_in %}
|
||||
<a href="{{ url_for('login') }}">log in</a>
|
||||
{% else %}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
{% for message in get_flashed_messages() %}
|
||||
<div class=flash>{{ message }}</div>
|
||||
<div class="flash">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
14
examples/flaskr/flaskr/templates/login.html
Normal file
14
examples/flaskr/flaskr/templates/login.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h2>Login</h2>
|
||||
{% if error %}<p class="error"><strong>Error:</strong> {{ error }}{% endif %}
|
||||
<form action="{{ url_for('login') }}" method="post">
|
||||
<dl>
|
||||
<dt>Username:
|
||||
<dd><input type="text" name="username">
|
||||
<dt>Password:
|
||||
<dd><input type="password" name="password">
|
||||
<dd><input type="submit" value="Login">
|
||||
</dl>
|
||||
</form>
|
||||
{% endblock %}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue