forked from orbit-oss/flask
Improved documentation, added a contextmanager for request binding
This commit is contained in:
parent
44b42e0fbd
commit
3b36bef2e6
4 changed files with 198 additions and 28 deletions
154
docs/api.rst
154
docs/api.rst
|
|
@ -8,8 +8,158 @@ parts where Flask depends on external libraries, we document the most
|
||||||
important right here and provide links to the canonical documentation.
|
important right here and provide links to the canonical documentation.
|
||||||
|
|
||||||
|
|
||||||
General Structure
|
Application Object
|
||||||
-----------------
|
------------------
|
||||||
|
|
||||||
.. autoclass:: Flask
|
.. autoclass:: Flask
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Incoming Request Data
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. class:: request
|
||||||
|
|
||||||
|
To access incoming request data, you can use the global `request`
|
||||||
|
object. Flask parses incoming request data for you and gives you
|
||||||
|
access to it through that global object. Internally Flask makes
|
||||||
|
sure that you always get the correct data for the active thread if you
|
||||||
|
are in a multithreaded environment.
|
||||||
|
|
||||||
|
The request object is an instance of a :class:`~werkzeug.Request`
|
||||||
|
subclass and provides all of the attributes Werkzeug defines. This
|
||||||
|
just shows a quick overview of the most important ones.
|
||||||
|
|
||||||
|
.. attribute:: form
|
||||||
|
|
||||||
|
A :class:`~werkzeug.MultiDict` with the parsed form data from `POST`
|
||||||
|
or `PUT` requests. Please keep in mind that file uploads will not
|
||||||
|
end up here, but instead in the :attr:`files` attribute.
|
||||||
|
|
||||||
|
.. attribute:: args
|
||||||
|
|
||||||
|
A :class:`~werkzeug.MultiDict` with the parsed contents of the query
|
||||||
|
string. (The part in the URL after the question mark).
|
||||||
|
|
||||||
|
.. attribute:: values
|
||||||
|
|
||||||
|
A :class:`~werkzeug.CombinedMultiDict` with the contents of both
|
||||||
|
:attr:`form` and :attr:`args`.
|
||||||
|
|
||||||
|
.. attribute:: cookies
|
||||||
|
|
||||||
|
A :class:`dict` with the contents of all cookies transmitted with
|
||||||
|
the request.
|
||||||
|
|
||||||
|
.. attribute:: stream
|
||||||
|
|
||||||
|
If the incoming form data was not encoded with a known encoding (for
|
||||||
|
example it was transmitted as JSON) the data is stored unmodified in
|
||||||
|
this stream for consumption. For example to read the incoming
|
||||||
|
request data as JSON, one can do the following::
|
||||||
|
|
||||||
|
json_body = simplejson.load(request.stream)
|
||||||
|
|
||||||
|
.. attribute:: files
|
||||||
|
|
||||||
|
A :class:`~werkzeug.MultiDict` with files uploaded as part of a
|
||||||
|
`POST` or `PUT` request. Each file is stored as
|
||||||
|
:class:`~werkzeug.FileStorage` object. It basically behaves like a
|
||||||
|
standard file object you know from Python, with the difference that
|
||||||
|
it also has a :meth:`~werkzeug.FileStorage.save` function that can
|
||||||
|
store the file on the filesystem.
|
||||||
|
|
||||||
|
.. attribute:: method
|
||||||
|
|
||||||
|
The current request method (``POST``, ``GET`` etc.)
|
||||||
|
|
||||||
|
.. attribute:: path
|
||||||
|
.. attribute:: script_root
|
||||||
|
.. attribute:: url
|
||||||
|
.. attribute:: base_url
|
||||||
|
.. attribute:: url_root
|
||||||
|
|
||||||
|
Provides different ways to look at the current URL. Imagine your
|
||||||
|
application is listening on the following URL::
|
||||||
|
|
||||||
|
http://www.example.com/myapplication
|
||||||
|
|
||||||
|
And a user requests the following URL::
|
||||||
|
|
||||||
|
http://www.example.com/myapplication/page.html?x=y
|
||||||
|
|
||||||
|
In this case the values of the above mentioned attributes would be
|
||||||
|
the following:
|
||||||
|
|
||||||
|
============= ======================================================
|
||||||
|
`path` ``/page.html``
|
||||||
|
`script_root` ``/myapplication``
|
||||||
|
`url` ``http://www.example.com/myapplication/page.html``
|
||||||
|
`base_url` ``http://www.example.com/myapplication/page.html?x=y``
|
||||||
|
`root_url` ``http://www.example.com/myapplication/``
|
||||||
|
============= ======================================================
|
||||||
|
|
||||||
|
|
||||||
|
Sessions
|
||||||
|
--------
|
||||||
|
|
||||||
|
If you have the :attr:`Flask.secret_key` set you can use sessions in Flask
|
||||||
|
applications. A session basically makes it possible to remember
|
||||||
|
information from one request to another. The way Flask does this is by
|
||||||
|
using a signed cookie. So the user can look at the session contents, but
|
||||||
|
not modify it unless he knows the secret key, so make sure to set that to
|
||||||
|
something complex and unguessable.
|
||||||
|
|
||||||
|
To access the current session you can use the :class:`session` object:
|
||||||
|
|
||||||
|
.. class:: session
|
||||||
|
|
||||||
|
The session object works pretty much like an ordinary dict, with the
|
||||||
|
difference that it keeps track on modifications.
|
||||||
|
|
||||||
|
The following attributes are interesting:
|
||||||
|
|
||||||
|
.. attribute:: new
|
||||||
|
|
||||||
|
`True` if the session is new, `False` otherwise.
|
||||||
|
|
||||||
|
.. attribute:: modified
|
||||||
|
|
||||||
|
`True` if the session object detected a modification. Be advised
|
||||||
|
that modifications on mutable structures are not picked up
|
||||||
|
automatically, in that situation you have to explicitly set the
|
||||||
|
attribute to `True` yourself. Here an example::
|
||||||
|
|
||||||
|
# this change is not picked up because a mutable object (here
|
||||||
|
# a list) is changed.
|
||||||
|
session['objects'].append(42)
|
||||||
|
# so mark it as modified yourself
|
||||||
|
session.modified = True
|
||||||
|
|
||||||
|
|
||||||
|
Useful Functions and Classes
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. autofunction:: url_for
|
||||||
|
|
||||||
|
.. autofunction:: abort
|
||||||
|
|
||||||
|
.. autofunction:: redirect
|
||||||
|
|
||||||
|
.. autofunction:: escape
|
||||||
|
|
||||||
|
.. autoclass:: Markup
|
||||||
|
:members: escape, unescape, striptags
|
||||||
|
|
||||||
|
Message Flashing
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autofunction:: flash
|
||||||
|
|
||||||
|
.. autofunction:: get_flashed_messages
|
||||||
|
|
||||||
|
Template Rendering
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. autofunction:: render_template
|
||||||
|
|
||||||
|
.. autofunction:: render_template_string
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import sys, os
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.autodoc']
|
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
@ -234,3 +234,8 @@ latex_documents = [
|
||||||
|
|
||||||
# The depth of the table of contents in toc.ncx.
|
# The depth of the table of contents in toc.ncx.
|
||||||
#epub_tocdepth = 3
|
#epub_tocdepth = 3
|
||||||
|
|
||||||
|
intersphinx_mapping = {
|
||||||
|
'http://docs.python.org/dev': None,
|
||||||
|
'http://werkzeug.pocoo.org/documentation/dev/': None
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ from hashlib import md5
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from flask import Flask, request, session, url_for, redirect, \
|
from flask import Flask, request, session, url_for, redirect, \
|
||||||
render_template, abort, g, flash, generate_password_hash, \
|
render_template, abort, g, flash
|
||||||
check_password_hash
|
from werkzeug import check_password_hash, generate_password_hash
|
||||||
|
|
||||||
|
|
||||||
# configuration
|
# configuration
|
||||||
|
|
|
||||||
61
flask.py
61
flask.py
|
|
@ -13,25 +13,17 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
from threading import local
|
from threading import local
|
||||||
|
from contextlib import contextmanager
|
||||||
from jinja2 import Environment, PackageLoader
|
from jinja2 import Environment, PackageLoader
|
||||||
from werkzeug import Request, Response, LocalStack, LocalProxy
|
from werkzeug import Request, Response, LocalStack, LocalProxy, \
|
||||||
|
create_environ, cached_property
|
||||||
from werkzeug.routing import Map, Rule
|
from werkzeug.routing import Map, Rule
|
||||||
from werkzeug.exceptions import HTTPException, InternalServerError
|
from werkzeug.exceptions import HTTPException, InternalServerError
|
||||||
from werkzeug.contrib.securecookie import SecureCookie
|
from werkzeug.contrib.securecookie import SecureCookie
|
||||||
|
|
||||||
# try to import the json helpers
|
|
||||||
try:
|
|
||||||
from simplejson import loads as load_json, dumps as dump_json
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
from json import loads as load_json, dumps as dump_json
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# utilities we import from Werkzeug and Jinja2 that are unused
|
# utilities we import from Werkzeug and Jinja2 that are unused
|
||||||
# in the module but are exported as public interface.
|
# in the module but are exported as public interface.
|
||||||
from werkzeug import abort, redirect, secure_filename, cached_property, \
|
from werkzeug import abort, redirect
|
||||||
html, import_string, generate_password_hash, check_password_hash
|
|
||||||
from jinja2 import Markup, escape
|
from jinja2 import Markup, escape
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -83,12 +75,6 @@ def url_for(endpoint, **values):
|
||||||
return _request_ctx_stack.top.url_adapter.build(endpoint, values)
|
return _request_ctx_stack.top.url_adapter.build(endpoint, values)
|
||||||
|
|
||||||
|
|
||||||
def jsonified(**values):
|
|
||||||
"""Returns a json response"""
|
|
||||||
return current_app.response_class(dump_json(values),
|
|
||||||
mimetype='application/json')
|
|
||||||
|
|
||||||
|
|
||||||
def flash(message):
|
def flash(message):
|
||||||
"""Flashes a message to the next request. In order to remove the
|
"""Flashes a message to the next request. In order to remove the
|
||||||
flashed message from the session and to display it to the user,
|
flashed message from the session and to display it to the user,
|
||||||
|
|
@ -113,6 +99,7 @@ def render_template(template_name, **context):
|
||||||
"""Renders a template from the template folder with the given
|
"""Renders a template from the template folder with the given
|
||||||
context.
|
context.
|
||||||
"""
|
"""
|
||||||
|
current_app.update_template_context(context)
|
||||||
return current_app.jinja_env.get_template(template_name).render(context)
|
return current_app.jinja_env.get_template(template_name).render(context)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -120,6 +107,7 @@ def render_template_string(source, **context):
|
||||||
"""Renders a template from the given template source string
|
"""Renders a template from the given template source string
|
||||||
with the given context.
|
with the given context.
|
||||||
"""
|
"""
|
||||||
|
current_app.update_template_context(context)
|
||||||
return current_app.jinja_env.from_string(source).render(context)
|
return current_app.jinja_env.from_string(source).render(context)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -220,9 +208,6 @@ class Flask(object):
|
||||||
**self.jinja_options)
|
**self.jinja_options)
|
||||||
self.jinja_env.globals.update(
|
self.jinja_env.globals.update(
|
||||||
url_for=url_for,
|
url_for=url_for,
|
||||||
request=request,
|
|
||||||
session=session,
|
|
||||||
g=g,
|
|
||||||
get_flashed_messages=get_flashed_messages
|
get_flashed_messages=get_flashed_messages
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -234,6 +219,15 @@ class Flask(object):
|
||||||
"""
|
"""
|
||||||
return PackageLoader(self.package_name)
|
return PackageLoader(self.package_name)
|
||||||
|
|
||||||
|
def update_template_context(self, context):
|
||||||
|
"""Update the template context with some commonly used variables.
|
||||||
|
This injects request, session and g into the template context.
|
||||||
|
"""
|
||||||
|
reqctx = _request_ctx_stack.top
|
||||||
|
context['request'] = reqctx.request
|
||||||
|
context['session'] = reqctx.session
|
||||||
|
context['g'] = reqctx.g
|
||||||
|
|
||||||
def run(self, host='localhost', port=5000, **options):
|
def run(self, host='localhost', port=5000, **options):
|
||||||
"""Runs the application on a local development server. If the
|
"""Runs the application on a local development server. If the
|
||||||
:attr:`debug` flag is set the server will automatically reload
|
:attr:`debug` flag is set the server will automatically reload
|
||||||
|
|
@ -443,17 +437,38 @@ class Flask(object):
|
||||||
|
|
||||||
app.wsgi_app = MyMiddleware(app.wsgi_app)
|
app.wsgi_app = MyMiddleware(app.wsgi_app)
|
||||||
"""
|
"""
|
||||||
_request_ctx_stack.push(_RequestContext(self, environ))
|
with self.request_context(environ):
|
||||||
try:
|
|
||||||
rv = self.preprocess_request()
|
rv = self.preprocess_request()
|
||||||
if rv is None:
|
if rv is None:
|
||||||
rv = self.dispatch_request()
|
rv = self.dispatch_request()
|
||||||
response = self.make_response(rv)
|
response = self.make_response(rv)
|
||||||
response = self.process_response(response)
|
response = self.process_response(response)
|
||||||
return response(environ, start_response)
|
return response(environ, start_response)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def request_context(self, environ):
|
||||||
|
"""Creates a request context from the given environment and binds
|
||||||
|
it to the current context. This must be used in combination with
|
||||||
|
the `with` statement because the request is only bound to the
|
||||||
|
current context for the duration of the `with` block.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
with app.request_context(environ):
|
||||||
|
do_something_with(request)
|
||||||
|
"""
|
||||||
|
_request_ctx_stack.push(_RequestContext(self, environ))
|
||||||
|
try:
|
||||||
|
yield
|
||||||
finally:
|
finally:
|
||||||
_request_ctx_stack.pop()
|
_request_ctx_stack.pop()
|
||||||
|
|
||||||
|
def test_request_context(self, *args, **kwargs):
|
||||||
|
"""Creates a WSGI environment from the given values (see
|
||||||
|
:func:`werkzeug.create_environ` for more information).
|
||||||
|
"""
|
||||||
|
return self.request_context(create_environ(*args, **kwargs))
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
"""Shortcut for :attr:`wsgi_app`"""
|
"""Shortcut for :attr:`wsgi_app`"""
|
||||||
return self.wsgi_app(environ, start_response)
|
return self.wsgi_app(environ, start_response)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue