Preparar para publicar en Read the Docs
Signed-off-by: Edgar Alvarado Taleno <edgar.alvaradotaleno@ucr.ac.cr>
This commit is contained in:
parent
b78b5a210b
commit
77f3f78332
190 changed files with 48425 additions and 102 deletions
189
flask-docs/_sources/patterns/appdispatch.rst.txt
Normal file
189
flask-docs/_sources/patterns/appdispatch.rst.txt
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
Application Dispatching
|
||||
=======================
|
||||
|
||||
Application dispatching is the process of combining multiple Flask
|
||||
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 :doc:`packages` is that in this case you
|
||||
are running the same or different Flask applications that are entirely
|
||||
isolated from each other. They run different configurations and are
|
||||
dispatched on the WSGI level.
|
||||
|
||||
|
||||
Working with this Document
|
||||
--------------------------
|
||||
|
||||
Each of the techniques and examples below results in an ``application``
|
||||
object that can be run with any WSGI server. For development, use the
|
||||
``flask run`` command to start a development server. For production, see
|
||||
:doc:`/deploying/index`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
return 'Hello World!'
|
||||
|
||||
|
||||
Combining Applications
|
||||
----------------------
|
||||
|
||||
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 is
|
||||
dispatched based on prefix.
|
||||
|
||||
For example you could have your main application run on ``/`` and your
|
||||
backend interface on ``/backend``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
||||
from frontend_app import application as frontend
|
||||
from backend_app import application as backend
|
||||
|
||||
application = DispatcherMiddleware(frontend, {
|
||||
'/backend': backend
|
||||
})
|
||||
|
||||
|
||||
Dispatch by Subdomain
|
||||
---------------------
|
||||
|
||||
Sometimes you might want to use multiple instances of the same application
|
||||
with different configurations. Assuming the application is created inside
|
||||
a function and you can call that function to instantiate it, that is
|
||||
really easy to implement. In order to develop your application to support
|
||||
creating new instances in functions have a look at the
|
||||
:doc:`appfactories` pattern.
|
||||
|
||||
A very common example would be creating applications per subdomain. For
|
||||
instance you configure your webserver to dispatch all requests for all
|
||||
subdomains to your application and you then use the subdomain information
|
||||
to create user-specific instances. Once you have your server set up to
|
||||
listen on all subdomains you can use a very simple WSGI application to do
|
||||
the dynamic application creation.
|
||||
|
||||
The perfect level for abstraction in that regard is the WSGI layer. You
|
||||
write your own WSGI application that looks at the request that comes and
|
||||
delegates it to your Flask application. If that application does not
|
||||
exist yet, it is dynamically created and remembered.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from threading import Lock
|
||||
|
||||
class SubdomainDispatcher:
|
||||
|
||||
def __init__(self, domain, create_app):
|
||||
self.domain = domain
|
||||
self.create_app = create_app
|
||||
self.lock = Lock()
|
||||
self.instances = {}
|
||||
|
||||
def get_application(self, host):
|
||||
host = host.split(':')[0]
|
||||
assert host.endswith(self.domain), 'Configuration error'
|
||||
subdomain = host[:-len(self.domain)].rstrip('.')
|
||||
with self.lock:
|
||||
app = self.instances.get(subdomain)
|
||||
if app is None:
|
||||
app = self.create_app(subdomain)
|
||||
self.instances[subdomain] = app
|
||||
return app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
app = self.get_application(environ['HTTP_HOST'])
|
||||
return app(environ, start_response)
|
||||
|
||||
|
||||
This dispatcher can then be used like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from myapplication import create_app, get_user_for_subdomain
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
def make_app(subdomain):
|
||||
user = get_user_for_subdomain(subdomain)
|
||||
if user is None:
|
||||
# if there is no user for that subdomain we still have
|
||||
# to return a WSGI application that handles that request.
|
||||
# We can then just return the NotFound() exception as
|
||||
# application which will render a default 404 page.
|
||||
# You might also redirect the user to the main page then
|
||||
return NotFound()
|
||||
|
||||
# otherwise create the application for the specific user
|
||||
return create_app(user)
|
||||
|
||||
application = SubdomainDispatcher('example.com', make_app)
|
||||
|
||||
|
||||
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
|
||||
request path up to the first slash.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from threading import Lock
|
||||
from wsgiref.util import shift_path_info
|
||||
|
||||
class PathDispatcher:
|
||||
|
||||
def __init__(self, default_app, create_app):
|
||||
self.default_app = default_app
|
||||
self.create_app = create_app
|
||||
self.lock = Lock()
|
||||
self.instances = {}
|
||||
|
||||
def get_application(self, prefix):
|
||||
with self.lock:
|
||||
app = self.instances.get(prefix)
|
||||
if app is None:
|
||||
app = self.create_app(prefix)
|
||||
if app is not None:
|
||||
self.instances[prefix] = app
|
||||
return app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
app = self.get_application(_peek_path_info(environ))
|
||||
if app is not None:
|
||||
shift_path_info(environ)
|
||||
else:
|
||||
app = self.default_app
|
||||
return app(environ, start_response)
|
||||
|
||||
def _peek_path_info(environ):
|
||||
segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
|
||||
if segments:
|
||||
return segments[0]
|
||||
|
||||
return None
|
||||
|
||||
The big difference between this and the subdomain one is that this one
|
||||
falls back to another application if the creator function returns ``None``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from myapplication import create_app, default_app, get_user_for_prefix
|
||||
|
||||
def make_app(prefix):
|
||||
user = get_user_for_prefix(prefix)
|
||||
if user is not None:
|
||||
return create_app(user)
|
||||
|
||||
application = PathDispatcher(default_app, make_app)
|
||||
118
flask-docs/_sources/patterns/appfactories.rst.txt
Normal file
118
flask-docs/_sources/patterns/appfactories.rst.txt
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
Application Factories
|
||||
=====================
|
||||
|
||||
If you are already using packages and blueprints for your application
|
||||
(:doc:`/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 app later.
|
||||
|
||||
So why would you want to do this?
|
||||
|
||||
1. Testing. You can have instances of the application with different
|
||||
settings to test every case.
|
||||
2. Multiple instances. Imagine you want to run different versions of the
|
||||
same application. Of course you could have multiple instances with
|
||||
different configs set up in your webserver, but if you use factories,
|
||||
you can have multiple instances of the same application running in the
|
||||
same application process which can be handy.
|
||||
|
||||
So how would you then actually implement that?
|
||||
|
||||
Basic Factories
|
||||
---------------
|
||||
|
||||
The idea is to set up the application in a function. Like this::
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile(config_filename)
|
||||
|
||||
from yourapplication.model import db
|
||||
db.init_app(app)
|
||||
|
||||
from yourapplication.views.admin import admin
|
||||
from yourapplication.views.frontend import frontend
|
||||
app.register_blueprint(admin)
|
||||
app.register_blueprint(frontend)
|
||||
|
||||
return app
|
||||
|
||||
The downside is that you cannot use the application object in the blueprints
|
||||
at import time. You can however use it from within a request. How do you
|
||||
get access to the application with the config? Use
|
||||
:data:`~flask.current_app`::
|
||||
|
||||
from flask import current_app, Blueprint, render_template
|
||||
admin = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
|
||||
@admin.route('/')
|
||||
def index():
|
||||
return render_template(current_app.config['INDEX_TEMPLATE'])
|
||||
|
||||
Here we look up the name of a template in the config.
|
||||
|
||||
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 <https://flask-sqlalchemy.palletsprojects.com/>`_,
|
||||
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)
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
But, rather, in model.py (or equivalent)::
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
and in your application.py (or equivalent)::
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile(config_filename)
|
||||
|
||||
from yourapplication.model import db
|
||||
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.
|
||||
For more information about the design of extensions refer to :doc:`/extensiondev`.
|
||||
|
||||
Using Applications
|
||||
------------------
|
||||
|
||||
To run such an application, you can use the :command:`flask` command:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app hello run
|
||||
|
||||
Flask will automatically detect the factory if it is named
|
||||
``create_app`` or ``make_app`` in ``hello``. You can also pass arguments
|
||||
to the factory like this:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app 'hello:create_app(local_auth=True)' run
|
||||
|
||||
Then the ``create_app`` factory in ``hello`` is called with the keyword
|
||||
argument ``local_auth=True``. See :doc:`/cli` for more detail.
|
||||
|
||||
Factory Improvements
|
||||
--------------------
|
||||
|
||||
The factory function above is not very clever, but you can improve it.
|
||||
The following changes are straightforward to implement:
|
||||
|
||||
1. Make it possible to pass in configuration values for unit tests so that
|
||||
you don't have to create config files on the filesystem.
|
||||
2. Call a function from a blueprint when the application is setting up so
|
||||
that you have a place to modify attributes of the application (like
|
||||
hooking in before/after request handlers etc.)
|
||||
3. Add in WSGI middlewares when the application is being created if necessary.
|
||||
16
flask-docs/_sources/patterns/caching.rst.txt
Normal file
16
flask-docs/_sources/patterns/caching.rst.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Caching
|
||||
=======
|
||||
|
||||
When your application runs slow, throw some caches in. Well, at least
|
||||
it's the easiest way to speed up things. What does a cache do? Say you
|
||||
have a function that takes some time to complete but the results would
|
||||
still be good enough if they were 5 minutes old. So then the idea is that
|
||||
you actually put the result of that calculation into a cache for some
|
||||
time.
|
||||
|
||||
Flask itself does not provide caching for you, but `Flask-Caching`_, an
|
||||
extension for Flask does. Flask-Caching supports various backends, and it is
|
||||
even possible to develop your own caching backend.
|
||||
|
||||
|
||||
.. _Flask-Caching: https://flask-caching.readthedocs.io/en/latest/
|
||||
242
flask-docs/_sources/patterns/celery.rst.txt
Normal file
242
flask-docs/_sources/patterns/celery.rst.txt
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
Background Tasks with Celery
|
||||
============================
|
||||
|
||||
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.
|
||||
|
||||
`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. Read Celery's `First Steps with Celery`_ guide to learn how to use
|
||||
Celery itself.
|
||||
|
||||
.. _Celery: https://celery.readthedocs.io
|
||||
.. _First Steps with Celery: https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html
|
||||
|
||||
The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
|
||||
based on the information on this page, which also shows how to use JavaScript to submit
|
||||
tasks and poll for progress and results.
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Install Celery from PyPI, for example using pip:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install celery
|
||||
|
||||
|
||||
Integrate Celery with Flask
|
||||
---------------------------
|
||||
|
||||
You can use Celery without any integration with Flask, but it's convenient to configure
|
||||
it through Flask's config, and to let tasks access the Flask application.
|
||||
|
||||
Celery uses similar ideas to Flask, with a ``Celery`` app object that has configuration
|
||||
and registers tasks. While creating a Flask app, use the following code to create and
|
||||
configure a Celery app as well.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from celery import Celery, Task
|
||||
|
||||
def celery_init_app(app: Flask) -> Celery:
|
||||
class FlaskTask(Task):
|
||||
def __call__(self, *args: object, **kwargs: object) -> object:
|
||||
with app.app_context():
|
||||
return self.run(*args, **kwargs)
|
||||
|
||||
celery_app = Celery(app.name, task_cls=FlaskTask)
|
||||
celery_app.config_from_object(app.config["CELERY"])
|
||||
celery_app.set_default()
|
||||
app.extensions["celery"] = celery_app
|
||||
return celery_app
|
||||
|
||||
This creates and returns a ``Celery`` app object. Celery `configuration`_ is taken from
|
||||
the ``CELERY`` key in the Flask configuration. The Celery app is set as the default, so
|
||||
that it is seen during each request. The ``Task`` subclass automatically runs task
|
||||
functions with a Flask app context active, so that services like your database
|
||||
connections are available.
|
||||
|
||||
.. _configuration: https://celery.readthedocs.io/en/stable/userguide/configuration.html
|
||||
|
||||
Here's a basic ``example.py`` that configures Celery to use Redis for communication. We
|
||||
enable a result backend, but ignore results by default. This allows us to store results
|
||||
only for tasks where we care about the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_mapping(
|
||||
CELERY=dict(
|
||||
broker_url="redis://localhost",
|
||||
result_backend="redis://localhost",
|
||||
task_ignore_result=True,
|
||||
),
|
||||
)
|
||||
celery_app = celery_init_app(app)
|
||||
|
||||
Point the ``celery worker`` command at this and it will find the ``celery_app`` object.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ celery -A example worker --loglevel INFO
|
||||
|
||||
You can also run the ``celery beat`` command to run tasks on a schedule. See Celery's
|
||||
docs for more information about defining schedules.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ celery -A example beat --loglevel INFO
|
||||
|
||||
|
||||
Application Factory
|
||||
-------------------
|
||||
|
||||
When using the Flask application factory pattern, call the ``celery_init_app`` function
|
||||
inside the factory. It sets ``app.extensions["celery"]`` to the Celery app object, which
|
||||
can be used to get the Celery app from the Flask app returned by the factory.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def create_app() -> Flask:
|
||||
app = Flask(__name__)
|
||||
app.config.from_mapping(
|
||||
CELERY=dict(
|
||||
broker_url="redis://localhost",
|
||||
result_backend="redis://localhost",
|
||||
task_ignore_result=True,
|
||||
),
|
||||
)
|
||||
app.config.from_prefixed_env()
|
||||
celery_init_app(app)
|
||||
return app
|
||||
|
||||
To use ``celery`` commands, Celery needs an app object, but that's no longer directly
|
||||
available. Create a ``make_celery.py`` file that calls the Flask app factory and gets
|
||||
the Celery app from the returned Flask app.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from example import create_app
|
||||
|
||||
flask_app = create_app()
|
||||
celery_app = flask_app.extensions["celery"]
|
||||
|
||||
Point the ``celery`` command to this file.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ celery -A make_celery worker --loglevel INFO
|
||||
$ celery -A make_celery beat --loglevel INFO
|
||||
|
||||
|
||||
Defining Tasks
|
||||
--------------
|
||||
|
||||
Using ``@celery_app.task`` to decorate task functions requires access to the
|
||||
``celery_app`` object, which won't be available when using the factory pattern. It also
|
||||
means that the decorated tasks are tied to the specific Flask and Celery app instances,
|
||||
which could be an issue during testing if you change configuration for a test.
|
||||
|
||||
Instead, use Celery's ``@shared_task`` decorator. This creates task objects that will
|
||||
access whatever the "current app" is, which is a similar concept to Flask's blueprints
|
||||
and app context. This is why we called ``celery_app.set_default()`` above.
|
||||
|
||||
Here's an example task that adds two numbers together and returns the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from celery import shared_task
|
||||
|
||||
@shared_task(ignore_result=False)
|
||||
def add_together(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
Earlier, we configured Celery to ignore task results by default. Since we want to know
|
||||
the return value of this task, we set ``ignore_result=False``. On the other hand, a task
|
||||
that didn't need a result, such as sending an email, wouldn't set this.
|
||||
|
||||
|
||||
Calling Tasks
|
||||
-------------
|
||||
|
||||
The decorated function becomes a task object with methods to call it in the background.
|
||||
The simplest way is to use the ``delay(*args, **kwargs)`` method. See Celery's docs for
|
||||
more methods.
|
||||
|
||||
A Celery worker must be running to run the task. Starting a worker is shown in the
|
||||
previous sections.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import request
|
||||
|
||||
@app.post("/add")
|
||||
def start_add() -> dict[str, object]:
|
||||
a = request.form.get("a", type=int)
|
||||
b = request.form.get("b", type=int)
|
||||
result = add_together.delay(a, b)
|
||||
return {"result_id": result.id}
|
||||
|
||||
The route doesn't get the task's result immediately. That would defeat the purpose by
|
||||
blocking the response. Instead, we return the running task's result id, which we can use
|
||||
later to get the result.
|
||||
|
||||
|
||||
Getting Results
|
||||
---------------
|
||||
|
||||
To fetch the result of the task we started above, we'll add another route that takes the
|
||||
result id we returned before. We return whether the task is finished (ready), whether it
|
||||
finished successfully, and what the return value (or error) was if it is finished.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from celery.result import AsyncResult
|
||||
|
||||
@app.get("/result/<id>")
|
||||
def task_result(id: str) -> dict[str, object]:
|
||||
result = AsyncResult(id)
|
||||
return {
|
||||
"ready": result.ready(),
|
||||
"successful": result.successful(),
|
||||
"value": result.result if result.ready() else None,
|
||||
}
|
||||
|
||||
Now you can start the task using the first route, then poll for the result using the
|
||||
second route. This keeps the Flask request workers from being blocked waiting for tasks
|
||||
to finish.
|
||||
|
||||
The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
|
||||
using JavaScript to submit tasks and poll for progress and results.
|
||||
|
||||
|
||||
Passing Data to Tasks
|
||||
---------------------
|
||||
|
||||
The "add" task above took two integers as arguments. To pass arguments to tasks, Celery
|
||||
has to serialize them to a format that it can pass to other processes. Therefore,
|
||||
passing complex objects is not recommended. For example, it would be impossible to pass
|
||||
a SQLAlchemy model object, since that object is probably not serializable and is tied to
|
||||
the session that queried it.
|
||||
|
||||
Pass the minimal amount of data necessary to fetch or recreate any complex data within
|
||||
the task. Consider a task that will run when the logged in user asks for an archive of
|
||||
their data. The Flask request knows the logged in user, and has the user object queried
|
||||
from the database. It got that by querying the database for a given id, so the task can
|
||||
do the same thing. Pass the user's id rather than the user object.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@shared_task
|
||||
def generate_user_archive(user_id: str) -> None:
|
||||
user = db.session.get(User, user_id)
|
||||
...
|
||||
|
||||
generate_user_archive.delay(current_user.id)
|
||||
44
flask-docs/_sources/patterns/deferredcallbacks.rst.txt
Normal file
44
flask-docs/_sources/patterns/deferredcallbacks.rst.txt
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
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.
|
||||
|
||||
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 a :meth:`~flask.Flask.after_request`
|
||||
callback instead. However, sometimes moving code there makes it
|
||||
more complicated or awkward to reason about.
|
||||
|
||||
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::
|
||||
|
||||
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)
|
||||
return response
|
||||
|
||||
g.language = language
|
||||
56
flask-docs/_sources/patterns/favicon.rst.txt
Normal file
56
flask-docs/_sources/patterns/favicon.rst.txt
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
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
|
||||
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
|
||||
:file:`favicon.ico`.
|
||||
|
||||
Now, to get browsers to find your icon, the correct way is to add a link
|
||||
tag in your HTML. So, for example:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
|
||||
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 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::
|
||||
|
||||
app.add_url_rule(
|
||||
"/favicon.ico",
|
||||
endpoint="favicon",
|
||||
redirect_to=url_for("static", filename="favicon.ico"),
|
||||
)
|
||||
|
||||
If you want to save the extra redirect request you can also write a view
|
||||
using :func:`~flask.send_from_directory`::
|
||||
|
||||
import os
|
||||
from flask import send_from_directory
|
||||
|
||||
@app.route('/favicon.ico')
|
||||
def favicon():
|
||||
return send_from_directory(os.path.join(app.root_path, 'static'),
|
||||
'favicon.ico', mimetype='image/vnd.microsoft.icon')
|
||||
|
||||
We can leave out the explicit mimetype and it will be guessed, but we may
|
||||
as well specify it to avoid the extra guessing, as it will always be the
|
||||
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
|
||||
web server's documentation.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
* The `Favicon <https://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
Wikipedia
|
||||
182
flask-docs/_sources/patterns/fileuploads.rst.txt
Normal file
182
flask-docs/_sources/patterns/fileuploads.rst.txt
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
Uploading Files
|
||||
===============
|
||||
|
||||
Ah yes, the good old problem of file uploads. The basic idea of file
|
||||
uploads is actually quite simple. It basically works like this:
|
||||
|
||||
1. A ``<form>`` tag is marked with ``enctype=multipart/form-data``
|
||||
and an ``<input type=file>`` is placed in that form.
|
||||
2. The application accesses the file from the :attr:`~flask.request.files`
|
||||
dictionary on the request object.
|
||||
3. use the :meth:`~werkzeug.datastructures.FileStorage.save` method of the file to save
|
||||
the file permanently somewhere on the filesystem.
|
||||
|
||||
A Gentle Introduction
|
||||
---------------------
|
||||
|
||||
Let's start with a very basic application that uploads a file to a
|
||||
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, flash, request, redirect, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
UPLOAD_FOLDER = '/path/to/the/uploads'
|
||||
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
|
||||
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.
|
||||
|
||||
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:`security-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].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 the user does not select a file, the browser submits an
|
||||
# empty file without a 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))
|
||||
return redirect(url_for('download_file', name=filename))
|
||||
return '''
|
||||
<!doctype html>
|
||||
<title>Upload new File</title>
|
||||
<h1>Upload new File</h1>
|
||||
<form method=post enctype=multipart/form-data>
|
||||
<input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
</form>
|
||||
'''
|
||||
|
||||
So what does that :func:`~werkzeug.utils.secure_filename` function actually do?
|
||||
Now the problem is that there is that principle called "never trust user
|
||||
input". This is also true for the filename of an uploaded file. All
|
||||
submitted form data can be forged, and filenames can be dangerous. For
|
||||
the moment just remember: always use that function to secure a filename
|
||||
before storing it directly on the filesystem.
|
||||
|
||||
.. admonition:: Information for the Pros
|
||||
|
||||
So you're interested in what that :func:`~werkzeug.utils.secure_filename`
|
||||
function does and what the problem is if you're not using it? So just
|
||||
imagine someone would send the following information as `filename` to
|
||||
your application::
|
||||
|
||||
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 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 :)
|
||||
|
||||
Now let's look how that function works:
|
||||
|
||||
>>> secure_filename('../../../../home/username/.bashrc')
|
||||
'home_username_.bashrc'
|
||||
|
||||
We want to be able to serve the uploaded files so they can be downloaded
|
||||
by users. We'll define a ``download_file`` view to serve files in the
|
||||
upload folder by name. ``url_for("download_file", name=name)`` generates
|
||||
download URLs.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import send_from_directory
|
||||
|
||||
@app.route('/uploads/<name>')
|
||||
def download_file(name):
|
||||
return send_from_directory(app.config["UPLOAD_FOLDER"], name)
|
||||
|
||||
If you're using middleware or the HTTP server to serve files, you can
|
||||
register the ``download_file`` endpoint as ``build_only`` so ``url_for``
|
||||
will work without a view function.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.add_url_rule(
|
||||
"/uploads/<name>", endpoint="download_file", build_only=True
|
||||
)
|
||||
|
||||
|
||||
Improving Uploads
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
So how exactly does Flask handle uploads? Well it will store them in the
|
||||
webserver's memory if the files are reasonably small, otherwise in a
|
||||
temporary location (as returned by :func:`tempfile.gettempdir`). But how
|
||||
do you specify the maximum file size after which an upload is aborted? By
|
||||
default Flask will happily accept file uploads with an unlimited amount of
|
||||
memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH``
|
||||
config key::
|
||||
|
||||
from flask import Flask, Request
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000
|
||||
|
||||
The code above will limit the maximum allowed payload to 16 megabytes.
|
||||
If a larger file is transmitted, Flask will raise a
|
||||
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception.
|
||||
|
||||
.. admonition:: Connection Reset Issue
|
||||
|
||||
When using the local development server, you may get a connection
|
||||
reset error instead of a 413 response. You will get the correct
|
||||
status response when running the app with a production WSGI server.
|
||||
|
||||
This feature was added in Flask 0.6 but can be achieved in older versions
|
||||
as well by subclassing the request object. For more information on that
|
||||
consult the Werkzeug documentation on file handling.
|
||||
|
||||
|
||||
Upload Progress Bars
|
||||
--------------------
|
||||
|
||||
A while ago many developers had the idea to read the incoming file in
|
||||
small chunks and store the upload progress in the database to be able to
|
||||
poll the progress with JavaScript from the client. The client asks the
|
||||
server every 5 seconds how much it has transmitted, but this is
|
||||
something it should already know.
|
||||
|
||||
An Easier Solution
|
||||
------------------
|
||||
|
||||
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.
|
||||
|
||||
Because the common pattern for file uploads exists almost unchanged in all
|
||||
applications dealing with uploads, there are also some Flask extensions that
|
||||
implement a full fledged upload mechanism that allows controlling which
|
||||
file extensions are allowed to be uploaded.
|
||||
|
||||
.. _jQuery: https://jquery.com/
|
||||
148
flask-docs/_sources/patterns/flashing.rst.txt
Normal file
148
flask-docs/_sources/patterns/flashing.rst.txt
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
Message Flashing
|
||||
================
|
||||
|
||||
Good applications and user interfaces are all about feedback. If the user
|
||||
does not get enough feedback they will probably end up hating the
|
||||
application. Flask provides a really simple way to give feedback to a
|
||||
user with the flashing system. The flashing system basically makes it
|
||||
possible to record a message at the end of a request and access it next
|
||||
request and only next request. This is usually combined with a layout
|
||||
template that does this. 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
|
||||
---------------
|
||||
|
||||
So here is a full example::
|
||||
|
||||
from flask import Flask, flash, redirect, render_template, \
|
||||
request, url_for
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
error = None
|
||||
if request.method == 'POST':
|
||||
if request.form['username'] != 'admin' or \
|
||||
request.form['password'] != 'secret':
|
||||
error = 'Invalid credentials'
|
||||
else:
|
||||
flash('You were successfully logged in')
|
||||
return redirect(url_for('index'))
|
||||
return render_template('login.html', error=error)
|
||||
|
||||
And here is the :file:`layout.html` template which does the magic:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<!doctype html>
|
||||
<title>My Application</title>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<ul class=flashes>
|
||||
{% for message in messages %}
|
||||
<li>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% block body %}{% endblock %}
|
||||
|
||||
Here is the :file:`index.html` template which inherits from :file:`layout.html`:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h1>Overview</h1>
|
||||
<p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
|
||||
{% endblock %}
|
||||
|
||||
And here is the :file:`login.html` template which also inherits from
|
||||
:file:`layout.html`:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h1>Login</h1>
|
||||
{% if error %}
|
||||
<p class=error><strong>Error:</strong> {{ error }}
|
||||
{% endif %}
|
||||
<form method=post>
|
||||
<dl>
|
||||
<dt>Username:
|
||||
<dd><input type=text name=username value="{{
|
||||
request.form.username }}">
|
||||
<dt>Password:
|
||||
<dd><input type=password name=password>
|
||||
</dl>
|
||||
<p><input type=submit value=Login>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
Flashing With Categories
|
||||
------------------------
|
||||
|
||||
.. versionadded:: 0.3
|
||||
|
||||
It is also possible to provide categories when flashing a message. The
|
||||
default category if nothing is provided is ``'message'``. Alternative
|
||||
categories can be used to give the user better feedback. For example
|
||||
error messages could be displayed with a red background.
|
||||
|
||||
To flash a message with a different category, just use the second argument
|
||||
to the :func:`~flask.flash` function::
|
||||
|
||||
flash('Invalid password provided', 'error')
|
||||
|
||||
Inside the template you then have to tell the
|
||||
:func:`~flask.get_flashed_messages` function to also return the
|
||||
categories. The loop looks slightly different in that situation then:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<ul class=flashes>
|
||||
{% for category, message in messages %}
|
||||
<li class="{{ category }}">{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
This is just one example of how to render these flashed messages. One
|
||||
might also use the category to add a prefix such as
|
||||
``<strong>Error:</strong>`` to the message.
|
||||
|
||||
Filtering Flash Messages
|
||||
------------------------
|
||||
|
||||
.. versionadded:: 0.9
|
||||
|
||||
Optionally you can pass a list of categories which filters the results of
|
||||
:func:`~flask.get_flashed_messages`. This is useful if you wish to
|
||||
render each category in a separate block.
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% with errors = get_flashed_messages(category_filter=["error"]) %}
|
||||
{% if errors %}
|
||||
<div class="alert-message block-message error">
|
||||
<a class="close" href="#">×</a>
|
||||
<ul>
|
||||
{%- for msg in errors %}
|
||||
<li>{{ msg }}</li>
|
||||
{% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
40
flask-docs/_sources/patterns/index.rst.txt
Normal file
40
flask-docs/_sources/patterns/index.rst.txt
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
Patterns for Flask
|
||||
==================
|
||||
|
||||
Certain features and interactions are common enough that you will find
|
||||
them in most web applications. For example, many applications use a
|
||||
relational database and user authentication. They will open a database
|
||||
connection at the beginning of the request and get the information for
|
||||
the logged in user. At the end of the request, the database connection
|
||||
is closed.
|
||||
|
||||
These types of patterns may be a bit outside the scope of Flask itself,
|
||||
but Flask makes it easy to implement them. Some common patterns are
|
||||
collected in the following pages.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
packages
|
||||
appfactories
|
||||
appdispatch
|
||||
urlprocessors
|
||||
sqlite3
|
||||
sqlalchemy
|
||||
fileuploads
|
||||
caching
|
||||
viewdecorators
|
||||
wtforms
|
||||
templateinheritance
|
||||
flashing
|
||||
javascript
|
||||
lazyloading
|
||||
mongoengine
|
||||
favicon
|
||||
streaming
|
||||
deferredcallbacks
|
||||
methodoverrides
|
||||
requestchecksum
|
||||
celery
|
||||
subclassing
|
||||
singlepageapplications
|
||||
259
flask-docs/_sources/patterns/javascript.rst.txt
Normal file
259
flask-docs/_sources/patterns/javascript.rst.txt
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
JavaScript, ``fetch``, and JSON
|
||||
===============================
|
||||
|
||||
You may want to make your HTML page dynamic, by changing data without
|
||||
reloading the entire page. Instead of submitting an HTML ``<form>`` and
|
||||
performing a redirect to re-render the template, you can add
|
||||
`JavaScript`_ that calls |fetch|_ and replaces content on the page.
|
||||
|
||||
|fetch|_ is the modern, built-in JavaScript solution to making
|
||||
requests from a page. You may have heard of other "AJAX" methods and
|
||||
libraries, such as |XHR|_ or `jQuery`_. These are no longer needed in
|
||||
modern browsers, although you may choose to use them or another library
|
||||
depending on your application's requirements. These docs will only focus
|
||||
on built-in JavaScript features.
|
||||
|
||||
.. _JavaScript: https://developer.mozilla.org/Web/JavaScript
|
||||
.. |fetch| replace:: ``fetch()``
|
||||
.. _fetch: https://developer.mozilla.org/Web/API/Fetch_API
|
||||
.. |XHR| replace:: ``XMLHttpRequest()``
|
||||
.. _XHR: https://developer.mozilla.org/Web/API/XMLHttpRequest
|
||||
.. _jQuery: https://jquery.com/
|
||||
|
||||
|
||||
Rendering Templates
|
||||
-------------------
|
||||
|
||||
It is important to understand the difference between templates and
|
||||
JavaScript. Templates are rendered on the server, before the response is
|
||||
sent to the user's browser. JavaScript runs in the user's browser, after
|
||||
the template is rendered and sent. Therefore, it is impossible to use
|
||||
JavaScript to affect how the Jinja template is rendered, but it is
|
||||
possible to render data into the JavaScript that will run.
|
||||
|
||||
To provide data to JavaScript when rendering the template, use the
|
||||
:func:`~jinja-filters.tojson` filter in a ``<script>`` block. This will
|
||||
convert the data to a valid JavaScript object, and ensure that any
|
||||
unsafe HTML characters are rendered safely. If you do not use the
|
||||
``tojson`` filter, you will get a ``SyntaxError`` in the browser
|
||||
console.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
data = generate_report()
|
||||
return render_template("report.html", chart_data=data)
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<script>
|
||||
const chart_data = {{ chart_data|tojson }}
|
||||
chartLib.makeChart(chart_data)
|
||||
</script>
|
||||
|
||||
A less common pattern is to add the data to a ``data-`` attribute on an
|
||||
HTML tag. In this case, you must use single quotes around the value, not
|
||||
double quotes, otherwise you will produce invalid or unsafe HTML.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<div data-chart='{{ chart_data|tojson }}'></div>
|
||||
|
||||
|
||||
Generating URLs
|
||||
---------------
|
||||
|
||||
The other way to get data from the server to JavaScript is to make a
|
||||
request for it. First, you need to know the URL to request.
|
||||
|
||||
The simplest way to generate URLs is to continue to use
|
||||
:func:`~flask.url_for` when rendering the template. For example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const user_url = {{ url_for("user", id=current_user.id)|tojson }}
|
||||
fetch(user_url).then(...)
|
||||
|
||||
However, you might need to generate a URL based on information you only
|
||||
know in JavaScript. As discussed above, JavaScript runs in the user's
|
||||
browser, not as part of the template rendering, so you can't use
|
||||
``url_for`` at that point.
|
||||
|
||||
In this case, you need to know the "root URL" under which your
|
||||
application is served. In simple setups, this is ``/``, but it might
|
||||
also be something else, like ``https://example.com/myapp/``.
|
||||
|
||||
A simple way to tell your JavaScript code about this root is to set it
|
||||
as a global variable when rendering the template. Then you can use it
|
||||
when generating URLs from JavaScript.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const SCRIPT_ROOT = {{ request.script_root|tojson }}
|
||||
let user_id = ... // do something to get a user id from the page
|
||||
let user_url = `${SCRIPT_ROOT}/user/${user_id}`
|
||||
fetch(user_url).then(...)
|
||||
|
||||
|
||||
Making a Request with ``fetch``
|
||||
-------------------------------
|
||||
|
||||
|fetch|_ takes two arguments, a URL and an object with other options,
|
||||
and returns a |Promise|_. We won't cover all the available options, and
|
||||
will only use ``then()`` on the promise, not other callbacks or
|
||||
``await`` syntax. Read the linked MDN docs for more information about
|
||||
those features.
|
||||
|
||||
By default, the GET method is used. If the response contains JSON, it
|
||||
can be used with a ``then()`` callback chain.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const room_url = {{ url_for("room_detail", id=room.id)|tojson }}
|
||||
fetch(room_url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// data is a parsed JSON object
|
||||
})
|
||||
|
||||
To send data, use a data method such as POST, and pass the ``body``
|
||||
option. The most common types for data are form data or JSON data.
|
||||
|
||||
To send form data, pass a populated |FormData|_ object. This uses the
|
||||
same format as an HTML form, and would be accessed with ``request.form``
|
||||
in a Flask view.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
let data = new FormData()
|
||||
data.append("name", "Flask Room")
|
||||
data.append("description", "Talk about Flask here.")
|
||||
fetch(room_url, {
|
||||
"method": "POST",
|
||||
"body": data,
|
||||
}).then(...)
|
||||
|
||||
In general, prefer sending request data as form data, as would be used
|
||||
when submitting an HTML form. JSON can represent more complex data, but
|
||||
unless you need that it's better to stick with the simpler format. When
|
||||
sending JSON data, the ``Content-Type: application/json`` header must be
|
||||
sent as well, otherwise Flask will return a 400 error.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
let data = {
|
||||
"name": "Flask Room",
|
||||
"description": "Talk about Flask here.",
|
||||
}
|
||||
fetch(room_url, {
|
||||
"method": "POST",
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"body": JSON.stringify(data),
|
||||
}).then(...)
|
||||
|
||||
.. |Promise| replace:: ``Promise``
|
||||
.. _Promise: https://developer.mozilla.org/Web/JavaScript/Reference/Global_Objects/Promise
|
||||
.. |FormData| replace:: ``FormData``
|
||||
.. _FormData: https://developer.mozilla.org/en-US/docs/Web/API/FormData
|
||||
|
||||
|
||||
Following Redirects
|
||||
-------------------
|
||||
|
||||
A response might be a redirect, for example if you logged in with
|
||||
JavaScript instead of a traditional HTML form, and your view returned
|
||||
a redirect instead of JSON. JavaScript requests do follow redirects, but
|
||||
they don't change the page. If you want to make the page change you can
|
||||
inspect the response and apply the redirect manually.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
fetch("/login", {"body": ...}).then(
|
||||
response => {
|
||||
if (response.redirected) {
|
||||
window.location = response.url
|
||||
} else {
|
||||
showLoginError()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Replacing Content
|
||||
-----------------
|
||||
|
||||
A response might be new HTML, either a new section of the page to add or
|
||||
replace, or an entirely new page. In general, if you're returning the
|
||||
entire page, it would be better to handle that with a redirect as shown
|
||||
in the previous section. The following example shows how to replace a
|
||||
``<div>`` with the HTML returned by a request.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div id="geology-fact">
|
||||
{{ include "geology_fact.html" }}
|
||||
</div>
|
||||
<script>
|
||||
const geology_url = {{ url_for("geology_fact")|tojson }}
|
||||
const geology_div = getElementById("geology-fact")
|
||||
fetch(geology_url)
|
||||
.then(response => response.text)
|
||||
.then(text => geology_div.innerHTML = text)
|
||||
</script>
|
||||
|
||||
|
||||
Return JSON from Views
|
||||
----------------------
|
||||
|
||||
To return a JSON object from your API view, you can directly return a
|
||||
dict from the view. It will be serialized to JSON automatically.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route("/user/<int:id>")
|
||||
def user_detail(id):
|
||||
user = User.query.get_or_404(id)
|
||||
return {
|
||||
"username": User.username,
|
||||
"email": User.email,
|
||||
"picture": url_for("static", filename=f"users/{id}/profile.png"),
|
||||
}
|
||||
|
||||
If you want to return another JSON type, use the
|
||||
:func:`~flask.json.jsonify` function, which creates a response object
|
||||
with the given data serialized to JSON.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@app.route("/users")
|
||||
def user_list():
|
||||
users = User.query.order_by(User.name).all()
|
||||
return jsonify([u.to_json() for u in users])
|
||||
|
||||
It is usually not a good idea to return file data in a JSON response.
|
||||
JSON cannot represent binary data directly, so it must be base64
|
||||
encoded, which can be slow, takes more bandwidth to send, and is not as
|
||||
easy to cache. Instead, serve files using one view, and generate a URL
|
||||
to the desired file to include in the JSON. Then the client can make a
|
||||
separate request to get the linked resource after getting the JSON.
|
||||
|
||||
|
||||
Receiving JSON in Views
|
||||
-----------------------
|
||||
|
||||
Use the :attr:`~flask.Request.json` property of the
|
||||
:data:`~flask.request` object to decode the request's body as JSON. If
|
||||
the body is not valid JSON, or the ``Content-Type`` header is not set to
|
||||
``application/json``, a 400 Bad Request error will be raised.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import request
|
||||
|
||||
@app.post("/user/<int:id>")
|
||||
def user_update(id):
|
||||
user = User.query.get_or_404(id)
|
||||
user.update_from_json(request.json)
|
||||
db.session.commit()
|
||||
return user.to_json()
|
||||
6
flask-docs/_sources/patterns/jquery.rst.txt
Normal file
6
flask-docs/_sources/patterns/jquery.rst.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
:orphan:
|
||||
|
||||
AJAX with jQuery
|
||||
================
|
||||
|
||||
Obsolete, see :doc:`/patterns/javascript` instead.
|
||||
109
flask-docs/_sources/patterns/lazyloading.rst.txt
Normal file
109
flask-docs/_sources/patterns/lazyloading.rst.txt
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
Lazily Loading Views
|
||||
====================
|
||||
|
||||
Flask is usually used with the decorators. Decorators are simple and you
|
||||
have the URL right next to the function that is called for that specific
|
||||
URL. However there is a downside to this approach: it means all your code
|
||||
that uses decorators has to be imported upfront or Flask will never
|
||||
actually find your function.
|
||||
|
||||
This can be a problem if your application has to import quick. It might
|
||||
have to do that on systems like Google's App Engine or other systems. So
|
||||
if you suddenly notice that your application outgrows this approach you
|
||||
can fall back to a centralized URL mapping.
|
||||
|
||||
The system that enables having a central URL map is the
|
||||
:meth:`~flask.Flask.add_url_rule` function. Instead of using decorators,
|
||||
you have a file that sets up the application with all URLs.
|
||||
|
||||
Converting to Centralized URL Map
|
||||
---------------------------------
|
||||
|
||||
Imagine the current application looks somewhat like this::
|
||||
|
||||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
pass
|
||||
|
||||
@app.route('/user/<username>')
|
||||
def user(username):
|
||||
pass
|
||||
|
||||
Then, with the centralized approach you would have one file with the views
|
||||
(:file:`views.py`) but without any decorator::
|
||||
|
||||
def index():
|
||||
pass
|
||||
|
||||
def user(username):
|
||||
pass
|
||||
|
||||
And then a file that sets up an application which maps the functions to
|
||||
URLs::
|
||||
|
||||
from flask import Flask
|
||||
from yourapplication import views
|
||||
app = Flask(__name__)
|
||||
app.add_url_rule('/', view_func=views.index)
|
||||
app.add_url_rule('/user/<username>', view_func=views.user)
|
||||
|
||||
Loading Late
|
||||
------------
|
||||
|
||||
So far we only split up the views and the routing, but the module is still
|
||||
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::
|
||||
|
||||
from werkzeug.utils import import_string, cached_property
|
||||
|
||||
class LazyView(object):
|
||||
|
||||
def __init__(self, import_name):
|
||||
self.__module__, self.__name__ = import_name.rsplit('.', 1)
|
||||
self.import_name = import_name
|
||||
|
||||
@cached_property
|
||||
def view(self):
|
||||
return import_string(self.import_name)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.view(*args, **kwargs)
|
||||
|
||||
What's important here is is that `__module__` and `__name__` are properly
|
||||
set. This is used by Flask internally to figure out how to name the
|
||||
URL rules in case you don't provide a name for the rule yourself.
|
||||
|
||||
Then you can define your central place to combine the views like this::
|
||||
|
||||
from flask import Flask
|
||||
from yourapplication.helpers import LazyView
|
||||
app = Flask(__name__)
|
||||
app.add_url_rule('/',
|
||||
view_func=LazyView('yourapplication.views.index'))
|
||||
app.add_url_rule('/user/<username>',
|
||||
view_func=LazyView('yourapplication.views.user'))
|
||||
|
||||
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. ::
|
||||
|
||||
def url(import_name, url_rules=[], **options):
|
||||
view = LazyView(f"yourapplication.{import_name}")
|
||||
for url_rule in url_rules:
|
||||
app.add_url_rule(url_rule, view_func=view, **options)
|
||||
|
||||
# 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
|
||||
request. The same goes for any kind of remaining decorator.
|
||||
42
flask-docs/_sources/patterns/methodoverrides.rst.txt
Normal file
42
flask-docs/_sources/patterns/methodoverrides.rst.txt
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
Adding HTTP Method Overrides
|
||||
============================
|
||||
|
||||
Some HTTP proxies do not support arbitrary HTTP methods or newer HTTP
|
||||
methods (such as PATCH). In that case it's possible to "proxy" HTTP
|
||||
methods through another HTTP method in total violation of the protocol.
|
||||
|
||||
The way this works is by letting the client do an HTTP POST request and
|
||||
set the ``X-HTTP-Method-Override`` header. Then the method is replaced
|
||||
with the header value before being passed to Flask.
|
||||
|
||||
This can be accomplished with an HTTP middleware::
|
||||
|
||||
class HTTPMethodOverrideMiddleware(object):
|
||||
allowed_methods = frozenset([
|
||||
'GET',
|
||||
'HEAD',
|
||||
'POST',
|
||||
'DELETE',
|
||||
'PUT',
|
||||
'PATCH',
|
||||
'OPTIONS'
|
||||
])
|
||||
bodyless_methods = frozenset(['GET', 'HEAD', 'OPTIONS', 'DELETE'])
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper()
|
||||
if method in self.allowed_methods:
|
||||
environ['REQUEST_METHOD'] = method
|
||||
if method in self.bodyless_methods:
|
||||
environ['CONTENT_LENGTH'] = '0'
|
||||
return self.app(environ, start_response)
|
||||
|
||||
To use this with Flask, wrap the app object with the middleware::
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app)
|
||||
103
flask-docs/_sources/patterns/mongoengine.rst.txt
Normal file
103
flask-docs/_sources/patterns/mongoengine.rst.txt
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
MongoDB with MongoEngine
|
||||
========================
|
||||
|
||||
Using a document database like MongoDB is a common alternative to
|
||||
relational SQL databases. This pattern shows how to use
|
||||
`MongoEngine`_, a document mapper library, to integrate with MongoDB.
|
||||
|
||||
A running MongoDB server and `Flask-MongoEngine`_ are required. ::
|
||||
|
||||
pip install flask-mongoengine
|
||||
|
||||
.. _MongoEngine: http://mongoengine.org
|
||||
.. _Flask-MongoEngine: https://flask-mongoengine.readthedocs.io
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Basic setup can be done by defining ``MONGODB_SETTINGS`` on
|
||||
``app.config`` and creating a ``MongoEngine`` instance. ::
|
||||
|
||||
from flask import Flask
|
||||
from flask_mongoengine import MongoEngine
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['MONGODB_SETTINGS'] = {
|
||||
"db": "myapp",
|
||||
}
|
||||
db = MongoEngine(app)
|
||||
|
||||
|
||||
Mapping Documents
|
||||
-----------------
|
||||
|
||||
To declare a model that represents a Mongo document, create a class that
|
||||
inherits from ``Document`` and declare each of the fields. ::
|
||||
|
||||
import mongoengine as me
|
||||
|
||||
class Movie(me.Document):
|
||||
title = me.StringField(required=True)
|
||||
year = me.IntField()
|
||||
rated = me.StringField()
|
||||
director = me.StringField()
|
||||
actors = me.ListField()
|
||||
|
||||
If the document has nested fields, use ``EmbeddedDocument`` to
|
||||
defined the fields of the embedded document and
|
||||
``EmbeddedDocumentField`` to declare it on the parent document. ::
|
||||
|
||||
class Imdb(me.EmbeddedDocument):
|
||||
imdb_id = me.StringField()
|
||||
rating = me.DecimalField()
|
||||
votes = me.IntField()
|
||||
|
||||
class Movie(me.Document):
|
||||
...
|
||||
imdb = me.EmbeddedDocumentField(Imdb)
|
||||
|
||||
|
||||
Creating Data
|
||||
-------------
|
||||
|
||||
Instantiate your document class with keyword arguments for the fields.
|
||||
You can also assign values to the field attributes after instantiation.
|
||||
Then call ``doc.save()``. ::
|
||||
|
||||
bttf = Movie(title="Back To The Future", year=1985)
|
||||
bttf.actors = [
|
||||
"Michael J. Fox",
|
||||
"Christopher Lloyd"
|
||||
]
|
||||
bttf.imdb = Imdb(imdb_id="tt0088763", rating=8.5)
|
||||
bttf.save()
|
||||
|
||||
|
||||
Queries
|
||||
-------
|
||||
|
||||
Use the class ``objects`` attribute to make queries. A keyword argument
|
||||
looks for an equal value on the field. ::
|
||||
|
||||
bttf = Movie.objects(title="Back To The Future").get_or_404()
|
||||
|
||||
Query operators may be used by concatenating them with the field name
|
||||
using a double-underscore. ``objects``, and queries returned by
|
||||
calling it, are iterable. ::
|
||||
|
||||
some_theron_movie = Movie.objects(actors__in=["Charlize Theron"]).first()
|
||||
|
||||
for recents in Movie.objects(year__gte=2017):
|
||||
print(recents.title)
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
There are many more ways to define and query documents with MongoEngine.
|
||||
For more information, check out the `official documentation
|
||||
<MongoEngine_>`_.
|
||||
|
||||
Flask-MongoEngine adds helpful utilities on top of MongoEngine. Check
|
||||
out their `documentation <Flask-MongoEngine_>`_ as well.
|
||||
133
flask-docs/_sources/patterns/packages.rst.txt
Normal file
133
flask-docs/_sources/patterns/packages.rst.txt
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
Large Applications as Packages
|
||||
==============================
|
||||
|
||||
Imagine a simple flask application structure that looks like this::
|
||||
|
||||
/yourapplication
|
||||
yourapplication.py
|
||||
/static
|
||||
style.css
|
||||
/templates
|
||||
layout.html
|
||||
index.html
|
||||
login.html
|
||||
...
|
||||
|
||||
While this is fine for small applications, for larger applications
|
||||
it's a good idea to use a package instead of a module.
|
||||
The :doc:`/tutorial/index` is structured to use the package pattern,
|
||||
see the :gh:`example code <examples/tutorial>`.
|
||||
|
||||
Simple Packages
|
||||
---------------
|
||||
|
||||
To convert that into a larger one, just create a new folder
|
||||
: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
|
||||
/static
|
||||
style.css
|
||||
/templates
|
||||
layout.html
|
||||
index.html
|
||||
login.html
|
||||
...
|
||||
|
||||
But how do you run your application now? The naive ``python
|
||||
yourapplication/__init__.py`` will not work. Let's just say that Python
|
||||
does not want modules in packages to be the startup file. But that is not
|
||||
a big problem, just add a new file called :file:`pyproject.toml` next to the inner
|
||||
:file:`yourapplication` folder with the following contents:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[project]
|
||||
name = "yourapplication"
|
||||
dependencies = [
|
||||
"flask",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["flit_core<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
|
||||
Install your application so it is importable:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install -e .
|
||||
|
||||
To use the ``flask`` command and run your application you need to set
|
||||
the ``--app`` option that tells Flask where to find the application
|
||||
instance:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app yourapplication 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
|
||||
: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 :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 :file:`__init__.py`::
|
||||
|
||||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
import yourapplication.views
|
||||
|
||||
And this is what :file:`views.py` would look like::
|
||||
|
||||
from yourapplication import app
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return 'Hello World!'
|
||||
|
||||
You should then end up with something like that::
|
||||
|
||||
/yourapplication
|
||||
pyproject.toml
|
||||
/yourapplication
|
||||
__init__.py
|
||||
views.py
|
||||
/static
|
||||
style.css
|
||||
/templates
|
||||
layout.html
|
||||
index.html
|
||||
login.html
|
||||
...
|
||||
|
||||
.. admonition:: Circular Imports
|
||||
|
||||
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 :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 :file:`__init__.py` and just
|
||||
ensuring the module is imported and we are doing that at the bottom of
|
||||
the file.
|
||||
|
||||
|
||||
Working with Blueprints
|
||||
-----------------------
|
||||
|
||||
If you have larger applications it's recommended to divide them into
|
||||
smaller groups where each group is implemented with the help of a
|
||||
blueprint. For a gentle introduction into this topic refer to the
|
||||
:doc:`/blueprints` chapter of the documentation.
|
||||
55
flask-docs/_sources/patterns/requestchecksum.rst.txt
Normal file
55
flask-docs/_sources/patterns/requestchecksum.rst.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
Request Content Checksums
|
||||
=========================
|
||||
|
||||
Various pieces of code can consume the request data and preprocess it.
|
||||
For instance JSON data ends up on the request object already read and
|
||||
processed, form data ends up there as well but goes through a different
|
||||
code path. This seems inconvenient when you want to calculate the
|
||||
checksum of the incoming request data. This is necessary sometimes for
|
||||
some APIs.
|
||||
|
||||
Fortunately this is however very simple to change by wrapping the input
|
||||
stream.
|
||||
|
||||
The following example calculates the SHA1 checksum of the incoming data as
|
||||
it gets read and stores it in the WSGI environment::
|
||||
|
||||
import hashlib
|
||||
|
||||
class ChecksumCalcStream(object):
|
||||
|
||||
def __init__(self, stream):
|
||||
self._stream = stream
|
||||
self._hash = hashlib.sha1()
|
||||
|
||||
def read(self, bytes):
|
||||
rv = self._stream.read(bytes)
|
||||
self._hash.update(rv)
|
||||
return rv
|
||||
|
||||
def readline(self, size_hint):
|
||||
rv = self._stream.readline(size_hint)
|
||||
self._hash.update(rv)
|
||||
return rv
|
||||
|
||||
def generate_checksum(request):
|
||||
env = request.environ
|
||||
stream = ChecksumCalcStream(env['wsgi.input'])
|
||||
env['wsgi.input'] = stream
|
||||
return stream._hash
|
||||
|
||||
To use this, all you need to do is to hook the calculating stream in
|
||||
before the request starts consuming data. (Eg: be careful accessing
|
||||
``request.form`` or anything of that nature. ``before_request_handlers``
|
||||
for instance should be careful not to access it).
|
||||
|
||||
Example usage::
|
||||
|
||||
@app.route('/special-api', methods=['POST'])
|
||||
def special_api():
|
||||
hash = generate_checksum(request)
|
||||
# Accessing this parses the input stream
|
||||
files = request.files
|
||||
# At this point the hash is fully constructed.
|
||||
checksum = hash.hexdigest()
|
||||
return f"Hash was: {checksum}"
|
||||
24
flask-docs/_sources/patterns/singlepageapplications.rst.txt
Normal file
24
flask-docs/_sources/patterns/singlepageapplications.rst.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Single-Page Applications
|
||||
========================
|
||||
|
||||
Flask can be used to serve Single-Page Applications (SPA) by placing static
|
||||
files produced by your frontend framework in a subfolder inside of your
|
||||
project. You will also need to create a catch-all endpoint that routes all
|
||||
requests to your SPA.
|
||||
|
||||
The following example demonstrates how to serve an SPA along with an API::
|
||||
|
||||
from flask import Flask, jsonify
|
||||
|
||||
app = Flask(__name__, static_folder='app', static_url_path="/app")
|
||||
|
||||
|
||||
@app.route("/heartbeat")
|
||||
def heartbeat():
|
||||
return jsonify({"status": "healthy"})
|
||||
|
||||
|
||||
@app.route('/', defaults={'path': ''})
|
||||
@app.route('/<path:path>')
|
||||
def catch_all(path):
|
||||
return app.send_static_file("index.html")
|
||||
214
flask-docs/_sources/patterns/sqlalchemy.rst.txt
Normal file
214
flask-docs/_sources/patterns/sqlalchemy.rst.txt
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
SQLAlchemy in Flask
|
||||
===================
|
||||
|
||||
Many people prefer `SQLAlchemy`_ for database access. In this case it's
|
||||
encouraged to use a package instead of a module for your flask application
|
||||
and drop the models into a separate module (:doc:`packages`). While that
|
||||
is not necessary, it makes a lot of sense.
|
||||
|
||||
There are four very common ways to use SQLAlchemy. I will outline each
|
||||
of them here:
|
||||
|
||||
Flask-SQLAlchemy Extension
|
||||
--------------------------
|
||||
|
||||
Because SQLAlchemy is a common database abstraction layer and object
|
||||
relational mapper that requires a little bit of configuration effort,
|
||||
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
|
||||
<https://pypi.org/project/Flask-SQLAlchemy/>`_.
|
||||
|
||||
.. _Flask-SQLAlchemy: https://flask-sqlalchemy.palletsprojects.com/
|
||||
|
||||
|
||||
Declarative
|
||||
-----------
|
||||
|
||||
The declarative extension in SQLAlchemy is the most recent method of using
|
||||
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's the example :file:`database.py` module for your application::
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker, declarative_base
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db')
|
||||
db_session = scoped_session(sessionmaker(autocommit=False,
|
||||
autoflush=False,
|
||||
bind=engine))
|
||||
Base = declarative_base()
|
||||
Base.query = db_session.query_property()
|
||||
|
||||
def init_db():
|
||||
# import all modules here that might define models so that
|
||||
# they will be registered properly on the metadata. Otherwise
|
||||
# you will have to import them first before calling init_db()
|
||||
import yourapplication.models
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
To define your models, just subclass the `Base` class that was created by
|
||||
the code above. If you are wondering why we don't have to care about
|
||||
threads here (like we did in the SQLite3 example above with the
|
||||
:data:`~flask.g` object): that's because SQLAlchemy does that for us
|
||||
already with the :class:`~sqlalchemy.orm.scoped_session`.
|
||||
|
||||
To use SQLAlchemy in a declarative way with your application, you just
|
||||
have to put the following code into your application module. Flask will
|
||||
automatically remove database sessions at the end of the request or
|
||||
when the application shuts down::
|
||||
|
||||
from yourapplication.database import db_session
|
||||
|
||||
@app.teardown_appcontext
|
||||
def shutdown_session(exception=None):
|
||||
db_session.remove()
|
||||
|
||||
Here is an example model (put this into :file:`models.py`, e.g.)::
|
||||
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from yourapplication.database import Base
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'users'
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(50), unique=True)
|
||||
email = Column(String(120), unique=True)
|
||||
|
||||
def __init__(self, name=None, email=None):
|
||||
self.name = name
|
||||
self.email = email
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.name!r}>'
|
||||
|
||||
To create the database you can use the `init_db` function:
|
||||
|
||||
>>> from yourapplication.database import init_db
|
||||
>>> init_db()
|
||||
|
||||
You can insert entries into the database like this:
|
||||
|
||||
>>> from yourapplication.database import db_session
|
||||
>>> from yourapplication.models import User
|
||||
>>> u = User('admin', 'admin@localhost')
|
||||
>>> db_session.add(u)
|
||||
>>> db_session.commit()
|
||||
|
||||
Querying is simple as well:
|
||||
|
||||
>>> User.query.all()
|
||||
[<User 'admin'>]
|
||||
>>> User.query.filter(User.name == 'admin').first()
|
||||
<User 'admin'>
|
||||
|
||||
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||
.. _declarative: https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
|
||||
|
||||
Manual Object Relational Mapping
|
||||
--------------------------------
|
||||
|
||||
Manual object relational mapping has a few upsides and a few downsides
|
||||
versus the declarative approach from above. The main difference is that
|
||||
you define tables and classes separately and map them together. It's more
|
||||
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 :file:`database.py` module for your application::
|
||||
|
||||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db')
|
||||
metadata = MetaData()
|
||||
db_session = scoped_session(sessionmaker(autocommit=False,
|
||||
autoflush=False,
|
||||
bind=engine))
|
||||
def init_db():
|
||||
metadata.create_all(bind=engine)
|
||||
|
||||
As in the declarative approach, you need to close the session after
|
||||
each request or application context shutdown. Put this into your
|
||||
application module::
|
||||
|
||||
from yourapplication.database import db_session
|
||||
|
||||
@app.teardown_appcontext
|
||||
def shutdown_session(exception=None):
|
||||
db_session.remove()
|
||||
|
||||
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
|
||||
from yourapplication.database import metadata, db_session
|
||||
|
||||
class User(object):
|
||||
query = db_session.query_property()
|
||||
|
||||
def __init__(self, name=None, email=None):
|
||||
self.name = name
|
||||
self.email = email
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.name!r}>'
|
||||
|
||||
users = Table('users', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('name', String(50), unique=True),
|
||||
Column('email', String(120), unique=True)
|
||||
)
|
||||
mapper(User, users)
|
||||
|
||||
Querying and inserting works exactly the same as in the example above.
|
||||
|
||||
|
||||
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, Table
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db')
|
||||
metadata = MetaData(bind=engine)
|
||||
|
||||
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
|
||||
connection first so that we can use a transaction:
|
||||
|
||||
>>> con = engine.connect()
|
||||
>>> con.execute(users.insert(), name='admin', email='admin@localhost')
|
||||
|
||||
SQLAlchemy will automatically commit for us.
|
||||
|
||||
To query your database, you use the engine directly or use a connection:
|
||||
|
||||
>>> users.select(users.c.id == 1).execute().first()
|
||||
(1, 'admin', 'admin@localhost')
|
||||
|
||||
These results are also dict-like tuples:
|
||||
|
||||
>>> r = users.select(users.c.id == 1).execute().first()
|
||||
>>> r['name']
|
||||
'admin'
|
||||
|
||||
You can also pass strings of SQL statements to the
|
||||
:meth:`~sqlalchemy.engine.base.Connection.execute` method:
|
||||
|
||||
>>> engine.execute('select * from users where id = :1', [1]).first()
|
||||
(1, 'admin', 'admin@localhost')
|
||||
|
||||
For more information about SQLAlchemy, head over to the
|
||||
`website <https://www.sqlalchemy.org/>`_.
|
||||
147
flask-docs/_sources/patterns/sqlite3.rst.txt
Normal file
147
flask-docs/_sources/patterns/sqlite3.rst.txt
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
Using SQLite 3 with Flask
|
||||
=========================
|
||||
|
||||
In Flask you can easily implement the opening of database connections on
|
||||
demand and closing them when the context dies (usually at the end of the
|
||||
request).
|
||||
|
||||
Here is a simple example of how you can use SQLite 3 with Flask::
|
||||
|
||||
import sqlite3
|
||||
from flask import g
|
||||
|
||||
DATABASE = '/path/to/database.db'
|
||||
|
||||
def get_db():
|
||||
db = getattr(g, '_database', None)
|
||||
if db is None:
|
||||
db = g._database = sqlite3.connect(DATABASE)
|
||||
return db
|
||||
|
||||
@app.teardown_appcontext
|
||||
def close_connection(exception):
|
||||
db = getattr(g, '_database', None)
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
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.
|
||||
|
||||
Example::
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
cur = get_db().cursor()
|
||||
...
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Please keep in mind that the teardown request and appcontext functions
|
||||
are always executed, even if a before-request handler failed or was
|
||||
never executed. Because of this we have to make sure here that the
|
||||
database is there before we close it.
|
||||
|
||||
Connect on Demand
|
||||
-----------------
|
||||
|
||||
The upside of this approach (connecting on first use) is that this will
|
||||
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::
|
||||
|
||||
with app.app_context():
|
||||
# now you can use get_db()
|
||||
|
||||
|
||||
Easy Querying
|
||||
-------------
|
||||
|
||||
Now in each request handling function you can access `get_db()` to get the
|
||||
current open database connection. To simplify working with SQLite, a
|
||||
row factory function is useful. It is executed for every result returned
|
||||
from the database to convert the result. For instance, in order to get
|
||||
dictionaries instead of tuples, this could be inserted into the ``get_db``
|
||||
function we created above::
|
||||
|
||||
def make_dicts(cursor, row):
|
||||
return dict((cursor.description[idx][0], value)
|
||||
for idx, value in enumerate(row))
|
||||
|
||||
db.row_factory = make_dicts
|
||||
|
||||
This will make the sqlite3 module return dicts for this database connection, which are much nicer to deal with. Even more simply, we could place this in ``get_db`` instead::
|
||||
|
||||
db.row_factory = sqlite3.Row
|
||||
|
||||
This would use Row objects rather than dicts to return the results of queries. These are ``namedtuple`` s, so we can access them either by index or by key. For example, assuming we have a ``sqlite3.Row`` called ``r`` for the rows ``id``, ``FirstName``, ``LastName``, and ``MiddleInitial``::
|
||||
|
||||
>>> # You can get values based on the row's name
|
||||
>>> r['FirstName']
|
||||
John
|
||||
>>> # Or, you can get them based on index
|
||||
>>> r[1]
|
||||
John
|
||||
# Row objects are also iterable:
|
||||
>>> for value in r:
|
||||
... print(value)
|
||||
1
|
||||
John
|
||||
Doe
|
||||
M
|
||||
|
||||
Additionally, it is a good idea to provide a query function that combines
|
||||
getting the cursor, executing and fetching the results::
|
||||
|
||||
def query_db(query, args=(), one=False):
|
||||
cur = get_db().execute(query, args)
|
||||
rv = cur.fetchall()
|
||||
cur.close()
|
||||
return (rv[0] if rv else None) if one else rv
|
||||
|
||||
This handy little function, in combination with a row factory, makes
|
||||
working with the database much more pleasant than it is by just using the
|
||||
raw cursor and connection objects.
|
||||
|
||||
Here is how you can use it::
|
||||
|
||||
for user in query_db('select * from users'):
|
||||
print(user['username'], 'has the id', user['user_id'])
|
||||
|
||||
Or if you just want a single result::
|
||||
|
||||
user = query_db('select * from users where username = ?',
|
||||
[the_username], one=True)
|
||||
if user is None:
|
||||
print('No such user')
|
||||
else:
|
||||
print(the_username, 'has the id', user['user_id'])
|
||||
|
||||
To pass variable parts to the SQL statement, use a question mark in the
|
||||
statement and pass in the arguments as a list. Never directly add them to
|
||||
the SQL statement with string formatting because this makes it possible
|
||||
to attack the application using `SQL Injections
|
||||
<https://en.wikipedia.org/wiki/SQL_injection>`_.
|
||||
|
||||
Initial Schemas
|
||||
---------------
|
||||
|
||||
Relational databases need schemas, so applications often ship a
|
||||
`schema.sql` file that creates the database. It's a good idea to provide
|
||||
a function that creates the database based on that schema. This function
|
||||
can do that for you::
|
||||
|
||||
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()
|
||||
|
||||
You can then create such a database from the Python shell:
|
||||
|
||||
>>> from yourapplication import init_db
|
||||
>>> init_db()
|
||||
85
flask-docs/_sources/patterns/streaming.rst.txt
Normal file
85
flask-docs/_sources/patterns/streaming.rst.txt
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
Streaming Contents
|
||||
==================
|
||||
|
||||
Sometimes you want to send an enormous amount of data to the client, much
|
||||
more than you want to keep in memory. When you are generating the data on
|
||||
the fly though, how do you send that back to the client without the
|
||||
roundtrip to the filesystem?
|
||||
|
||||
The answer is by using generators and direct responses.
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
This is a basic view function that generates a lot of CSV data on the fly.
|
||||
The trick is to have an inner function that uses a generator to generate
|
||||
data and to then invoke that function and pass it to a response object::
|
||||
|
||||
@app.route('/large.csv')
|
||||
def generate_large_csv():
|
||||
def generate():
|
||||
for row in iter_all_rows():
|
||||
yield f"{','.join(row)}\n"
|
||||
return generate(), {"Content-Type": "text/csv"}
|
||||
|
||||
Each ``yield`` expression is directly sent to the browser. Note though
|
||||
that some WSGI middlewares might break streaming, so be careful there in
|
||||
debug environments with profilers and other things you might have enabled.
|
||||
|
||||
Streaming from Templates
|
||||
------------------------
|
||||
|
||||
The Jinja2 template engine supports rendering a template piece by
|
||||
piece, returning an iterator of strings. Flask provides the
|
||||
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
|
||||
functions to make this easier to use.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import stream_template
|
||||
|
||||
@app.get("/timeline")
|
||||
def timeline():
|
||||
return stream_template("timeline.html")
|
||||
|
||||
The parts yielded by the render stream tend to match statement blocks in
|
||||
the template.
|
||||
|
||||
|
||||
Streaming with Context
|
||||
----------------------
|
||||
|
||||
The :data:`~flask.request` will not be active while the generator is
|
||||
running, because the view has already returned at that point. If you try
|
||||
to access ``request``, you'll get a ``RuntimeError``.
|
||||
|
||||
If your generator function relies on data in ``request``, use the
|
||||
:func:`~flask.stream_with_context` wrapper. This will keep the request
|
||||
context active during the generator.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import stream_with_context, request
|
||||
from markupsafe import escape
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
def generate():
|
||||
yield '<p>Hello '
|
||||
yield escape(request.args['name'])
|
||||
yield '!</p>'
|
||||
return stream_with_context(generate())
|
||||
|
||||
It can also be used as a decorator.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@stream_with_context
|
||||
def generate():
|
||||
...
|
||||
|
||||
return generate()
|
||||
|
||||
The :func:`~flask.stream_template` and
|
||||
:func:`~flask.stream_template_string` functions automatically
|
||||
use :func:`~flask.stream_with_context` if a request is active.
|
||||
17
flask-docs/_sources/patterns/subclassing.rst.txt
Normal file
17
flask-docs/_sources/patterns/subclassing.rst.txt
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.
|
||||
68
flask-docs/_sources/patterns/templateinheritance.rst.txt
Normal file
68
flask-docs/_sources/patterns/templateinheritance.rst.txt
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
Template Inheritance
|
||||
====================
|
||||
|
||||
The most powerful part of Jinja is template inheritance. Template inheritance
|
||||
allows you to build a base "skeleton" template that contains all the common
|
||||
elements of your site and defines **blocks** that child templates can override.
|
||||
|
||||
Sounds complicated but is very basic. It's easiest to understand it by starting
|
||||
with an example.
|
||||
|
||||
|
||||
Base Template
|
||||
-------------
|
||||
|
||||
This template, which we'll call :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:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<title>{% block title %}{% endblock %} - My Webpage</title>
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">{% block content %}{% endblock %}</div>
|
||||
<div id="footer">
|
||||
{% block footer %}
|
||||
© Copyright 2010 by <a href="http://domain.invalid/">you</a>.
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
In this example, the ``{% block %}`` tags define four blocks that child templates
|
||||
can fill in. All the `block` tag does is tell the template engine that a
|
||||
child template may override those portions of the template.
|
||||
|
||||
Child Template
|
||||
--------------
|
||||
|
||||
A child template might look like this:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Index{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<style type="text/css">
|
||||
.important { color: #336699; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h1>Index</h1>
|
||||
<p class="important">
|
||||
Welcome on my awesome homepage.
|
||||
{% endblock %}
|
||||
|
||||
The ``{% extends %}`` tag is the key here. It tells the template engine that
|
||||
this template "extends" another template. When the template system evaluates
|
||||
this template, first it locates the parent. The extends tag must be the
|
||||
first tag in the template. To render the contents of a block defined in
|
||||
the parent template, use ``{{ super() }}``.
|
||||
126
flask-docs/_sources/patterns/urlprocessors.rst.txt
Normal file
126
flask-docs/_sources/patterns/urlprocessors.rst.txt
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
Using URL Processors
|
||||
====================
|
||||
|
||||
.. versionadded:: 0.7
|
||||
|
||||
Flask 0.7 introduces the concept of URL processors. The idea is that you
|
||||
might have a bunch of resources with common parts in the URL that you
|
||||
don't always explicitly want to provide. For instance you might have a
|
||||
bunch of URLs that have the language code in it but you don't want to have
|
||||
to handle it in every single function yourself.
|
||||
|
||||
URL processors are especially helpful when combined with blueprints. We
|
||||
will handle both application specific URL processors here as well as
|
||||
blueprint specifics.
|
||||
|
||||
Internationalized Application URLs
|
||||
----------------------------------
|
||||
|
||||
Consider an application like this::
|
||||
|
||||
from flask import Flask, g
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/<lang_code>/')
|
||||
def index(lang_code):
|
||||
g.lang_code = lang_code
|
||||
...
|
||||
|
||||
@app.route('/<lang_code>/about')
|
||||
def about(lang_code):
|
||||
g.lang_code = lang_code
|
||||
...
|
||||
|
||||
This is an awful lot of repetition as you have to handle the language code
|
||||
setting on the :data:`~flask.g` object yourself in every single function.
|
||||
Sure, a decorator could be used to simplify this, but if you want to
|
||||
generate URLs from one function to another you would have to still provide
|
||||
the language code explicitly which can be annoying.
|
||||
|
||||
For the latter, this is where :func:`~flask.Flask.url_defaults` functions
|
||||
come in. They can automatically inject values into a call to
|
||||
:func:`~flask.url_for`. The code below checks if the
|
||||
language code is not yet in the dictionary of URL values and if the
|
||||
endpoint wants a value named ``'lang_code'``::
|
||||
|
||||
@app.url_defaults
|
||||
def add_language_code(endpoint, values):
|
||||
if 'lang_code' in values or not g.lang_code:
|
||||
return
|
||||
if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
|
||||
values['lang_code'] = g.lang_code
|
||||
|
||||
The method :meth:`~werkzeug.routing.Map.is_endpoint_expecting` of the URL
|
||||
map can be used to figure out if it would make sense to provide a language
|
||||
code for the given endpoint.
|
||||
|
||||
The reverse of that function are
|
||||
:meth:`~flask.Flask.url_value_preprocessor`\s. They are executed right
|
||||
after the request was matched and can execute code based on the URL
|
||||
values. The idea is that they pull information out of the values
|
||||
dictionary and put it somewhere else::
|
||||
|
||||
@app.url_value_preprocessor
|
||||
def pull_lang_code(endpoint, values):
|
||||
g.lang_code = values.pop('lang_code', None)
|
||||
|
||||
That way you no longer have to do the `lang_code` assignment to
|
||||
:data:`~flask.g` in every function. You can further improve that by
|
||||
writing your own decorator that prefixes URLs with the language code, but
|
||||
the more beautiful solution is using a blueprint. Once the
|
||||
``'lang_code'`` is popped from the values dictionary and it will no longer
|
||||
be forwarded to the view function reducing the code to this::
|
||||
|
||||
from flask import Flask, g
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.url_defaults
|
||||
def add_language_code(endpoint, values):
|
||||
if 'lang_code' in values or not g.lang_code:
|
||||
return
|
||||
if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
|
||||
values['lang_code'] = g.lang_code
|
||||
|
||||
@app.url_value_preprocessor
|
||||
def pull_lang_code(endpoint, values):
|
||||
g.lang_code = values.pop('lang_code', None)
|
||||
|
||||
@app.route('/<lang_code>/')
|
||||
def index():
|
||||
...
|
||||
|
||||
@app.route('/<lang_code>/about')
|
||||
def about():
|
||||
...
|
||||
|
||||
Internationalized Blueprint URLs
|
||||
--------------------------------
|
||||
|
||||
Because blueprints can automatically prefix all URLs with a common string
|
||||
it's easy to automatically do that for every function. Furthermore
|
||||
blueprints can have per-blueprint URL processors which removes a whole lot
|
||||
of logic from the :meth:`~flask.Flask.url_defaults` function because it no
|
||||
longer has to check if the URL is really interested in a ``'lang_code'``
|
||||
parameter::
|
||||
|
||||
from flask import Blueprint, g
|
||||
|
||||
bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>')
|
||||
|
||||
@bp.url_defaults
|
||||
def add_language_code(endpoint, values):
|
||||
values.setdefault('lang_code', g.lang_code)
|
||||
|
||||
@bp.url_value_preprocessor
|
||||
def pull_lang_code(endpoint, values):
|
||||
g.lang_code = values.pop('lang_code')
|
||||
|
||||
@bp.route('/')
|
||||
def index():
|
||||
...
|
||||
|
||||
@bp.route('/about')
|
||||
def about():
|
||||
...
|
||||
171
flask-docs/_sources/patterns/viewdecorators.rst.txt
Normal file
171
flask-docs/_sources/patterns/viewdecorators.rst.txt
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
View Decorators
|
||||
===============
|
||||
|
||||
Python has a really interesting feature called function decorators. This
|
||||
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. 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.
|
||||
|
||||
Login Required Decorator
|
||||
------------------------
|
||||
|
||||
So let's implement such a decorator. A decorator is a function that
|
||||
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 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
|
||||
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if g.user is None:
|
||||
return redirect(url_for('login', next=request.url))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
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
|
||||
-----------------
|
||||
|
||||
Imagine you have a view function that does an expensive calculation and
|
||||
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 :doc:`caching`.
|
||||
|
||||
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
|
||||
it is a little bit more complex, but the code should still be
|
||||
straightforward to read.
|
||||
|
||||
The decorated function will then work as follows
|
||||
|
||||
1. get the unique cache key for the current request based on the current
|
||||
path.
|
||||
2. get the value for that key from the cache. If the cache returned
|
||||
something we will return that value.
|
||||
3. otherwise the original function is called and the return value is
|
||||
stored in the cache for the timeout provided (by default 5 minutes).
|
||||
|
||||
Here the code::
|
||||
|
||||
from functools import wraps
|
||||
from flask import request
|
||||
|
||||
def cached(timeout=5 * 60, key='view/{}'):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
cache_key = key.format(request.path)
|
||||
rv = cache.get(cache_key)
|
||||
if rv is not None:
|
||||
return rv
|
||||
rv = f(*args, **kwargs)
|
||||
cache.set(cache_key, rv, timeout=timeout)
|
||||
return rv
|
||||
return decorated_function
|
||||
return decorator
|
||||
|
||||
Notice that this assumes an instantiated ``cache`` object is available, see
|
||||
:doc:`caching`.
|
||||
|
||||
|
||||
Templating Decorator
|
||||
--------------------
|
||||
|
||||
A common pattern invented by the TurboGears guys a while back is a
|
||||
templating decorator. The idea of that decorator is that you return a
|
||||
dictionary with the values passed to the template from the view function
|
||||
and the template is automatically rendered. With that, the following
|
||||
three examples do exactly the same::
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html', value=42)
|
||||
|
||||
@app.route('/')
|
||||
@templated('index.html')
|
||||
def index():
|
||||
return dict(value=42)
|
||||
|
||||
@app.route('/')
|
||||
@templated()
|
||||
def index():
|
||||
return dict(value=42)
|
||||
|
||||
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
|
||||
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 is the code for that decorator::
|
||||
|
||||
from functools import wraps
|
||||
from flask import request, render_template
|
||||
|
||||
def templated(template=None):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
template_name = template
|
||||
if template_name is None:
|
||||
template_name = f"{request.endpoint.replace('.', '/')}.html"
|
||||
ctx = f(*args, **kwargs)
|
||||
if ctx is None:
|
||||
ctx = {}
|
||||
elif not isinstance(ctx, dict):
|
||||
return ctx
|
||||
return render_template(template_name, **ctx)
|
||||
return decorated_function
|
||||
return decorator
|
||||
|
||||
|
||||
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::
|
||||
|
||||
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"
|
||||
126
flask-docs/_sources/patterns/wtforms.rst.txt
Normal file
126
flask-docs/_sources/patterns/wtforms.rst.txt
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
Form Validation with WTForms
|
||||
============================
|
||||
|
||||
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
|
||||
(:doc:`packages`) for that and adding a separate module for the
|
||||
forms.
|
||||
|
||||
.. admonition:: Getting the most out of WTForms with an Extension
|
||||
|
||||
The `Flask-WTF`_ extension expands on this pattern and adds a
|
||||
few little helpers that make working with forms and Flask more
|
||||
fun. You can get it from `PyPI
|
||||
<https://pypi.org/project/Flask-WTF/>`_.
|
||||
|
||||
.. _Flask-WTF: https://flask-wtf.readthedocs.io/
|
||||
|
||||
The Forms
|
||||
---------
|
||||
|
||||
This is an example form for a typical registration page::
|
||||
|
||||
from wtforms import Form, BooleanField, StringField, PasswordField, validators
|
||||
|
||||
class RegistrationForm(Form):
|
||||
username = StringField('Username', [validators.Length(min=4, max=25)])
|
||||
email = StringField('Email Address', [validators.Length(min=6, max=35)])
|
||||
password = PasswordField('New Password', [
|
||||
validators.DataRequired(),
|
||||
validators.EqualTo('confirm', message='Passwords must match')
|
||||
])
|
||||
confirm = PasswordField('Repeat Password')
|
||||
accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])
|
||||
|
||||
In the View
|
||||
-----------
|
||||
|
||||
In the view function, the usage of this form looks like this::
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
form = RegistrationForm(request.form)
|
||||
if request.method == 'POST' and form.validate():
|
||||
user = User(form.username.data, form.email.data,
|
||||
form.password.data)
|
||||
db_session.add(user)
|
||||
flash('Thanks for registering')
|
||||
return redirect(url_for('login'))
|
||||
return render_template('register.html', form=form)
|
||||
|
||||
Notice we're implying that the view is using SQLAlchemy here
|
||||
(:doc:`sqlalchemy`), 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``.
|
||||
2. to validate the data, call the :func:`~wtforms.form.Form.validate`
|
||||
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
|
||||
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 :file:`_formhelpers.html` template with such a macro:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% macro render_field(field) %}
|
||||
<dt>{{ field.label }}
|
||||
<dd>{{ field(**kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% endmacro %}
|
||||
|
||||
This macro accepts a couple of keyword arguments that are forwarded to
|
||||
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 strings,
|
||||
so we have to tell Jinja2 that this data is already HTML-escaped with
|
||||
the ``|safe`` filter.
|
||||
|
||||
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>
|
||||
<dl>
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.email) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.confirm) }}
|
||||
{{ render_field(form.accept_tos) }}
|
||||
</dl>
|
||||
<p><input type=submit value=Register>
|
||||
</form>
|
||||
|
||||
For more information about WTForms, head over to the `WTForms
|
||||
website`_.
|
||||
|
||||
.. _WTForms: https://wtforms.readthedocs.io/
|
||||
.. _WTForms website: https://wtforms.readthedocs.io/
|
||||
Loading…
Add table
Add a link
Reference in a new issue