forked from orbit-oss/flask
Fixed merging confict
This commit is contained in:
commit
eeb0e94951
9 changed files with 147 additions and 13 deletions
2
Makefile
2
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
.PHONY: clean-pyc test
|
||||
.PHONY: clean-pyc test upload-docs
|
||||
|
||||
all: clean-pyc test
|
||||
|
||||
|
|
|
|||
|
|
@ -183,6 +183,13 @@ To access the current session you can use the :class:`session` object:
|
|||
# so mark it as modified yourself
|
||||
session.modified = True
|
||||
|
||||
.. attribute:: permanent
|
||||
|
||||
If set to `True` the session life for
|
||||
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
|
||||
default is 31 days. If set to `False` (which is the default) the
|
||||
session will be deleted when the user closes the browser.
|
||||
|
||||
|
||||
Application Globals
|
||||
-------------------
|
||||
|
|
|
|||
|
|
@ -91,3 +91,52 @@ Here the code::
|
|||
|
||||
Notice that this assumes an instanciated `cache` object is available, see
|
||||
:ref:`caching-pattern` for more information.
|
||||
|
||||
|
||||
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 + ``'.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.
|
||||
|
||||
Here the code for that decorator::
|
||||
|
||||
from functools import wraps
|
||||
from flask import request
|
||||
|
||||
def templated(template=None):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
template_name = template
|
||||
if template_name is None:
|
||||
template_name = request.endpoint + '.html'
|
||||
ctx = f(*args, **kwargs)
|
||||
if ctx is None:
|
||||
ctx = {}
|
||||
return render_template(template_name, **ctx)
|
||||
return decorated_function
|
||||
return decorator
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ In the view function, the usage of this form looks like this::
|
|||
form.password.data)
|
||||
db_session.add(user)
|
||||
flash('Thanks for registering')
|
||||
redirect(url_for('login'))
|
||||
return redirect(url_for('login'))
|
||||
return render_template('register.html', form=form)
|
||||
|
||||
Notice that we are implying that the view is using SQLAlchemy here
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ The following converters exist:
|
|||
`path` like the default but also accepts slashes
|
||||
=========== ===========================================
|
||||
|
||||
.. _url-building:
|
||||
|
||||
URL Building
|
||||
````````````
|
||||
|
||||
|
|
@ -167,7 +169,8 @@ If it can match URLs, can it also generate them? Of course you can. To
|
|||
build a URL to a specific function you can use the :func:`~flask.url_for`
|
||||
function. It accepts the name of the function as first argument and a
|
||||
number of keyword arguments, each corresponding to the variable part of
|
||||
the URL rule. Here some examples:
|
||||
the URL rule. Unknown variable parts are appended to the URL as query
|
||||
parameter. Here some examples:
|
||||
|
||||
>>> from flask import Flask, url_for
|
||||
>>> app = Flask(__name__)
|
||||
|
|
@ -184,9 +187,11 @@ the URL rule. Here some examples:
|
|||
... print url_for('index')
|
||||
... print url_for('login')
|
||||
... print url_for('profile', username='John Doe')
|
||||
... print url_for('login', next='/')
|
||||
...
|
||||
/
|
||||
/login
|
||||
/login?next=/
|
||||
/user/John%20Doe
|
||||
|
||||
(This also uses the :meth:`~flask.Flask.test_request_context` method
|
||||
|
|
@ -452,7 +457,7 @@ transmitted in a `POST` or `PUT` request) you can use the
|
|||
:attr:`~flask.request.form` attribute. Here a full example of the two
|
||||
attributes mentioned above::
|
||||
|
||||
@app.route('/login', method=['POST', 'GET'])
|
||||
@app.route('/login', methods=['POST', 'GET'])
|
||||
def login():
|
||||
error = None
|
||||
if request.method == 'POST':
|
||||
|
|
|
|||
|
|
@ -195,3 +195,22 @@ suite.
|
|||
|
||||
.. _MiniTwit Example:
|
||||
http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/
|
||||
|
||||
|
||||
Other Testing Tricks
|
||||
--------------------
|
||||
|
||||
Besides using the test client we used above there is also the
|
||||
:meth:`~flask.Flask.test_request_context` method that in combination with
|
||||
the `with` statement can be used to activate a request context
|
||||
temporarily. With that you can access the :class:`~flask.request`,
|
||||
:class:`~flask.g` and :class:`~flask.session` objects like in view
|
||||
functions. Here a full example that showcases this::
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
with app.test_request_context('/?name=Peter'):
|
||||
assert flask.request.path == '/'
|
||||
assert flask.request.args['name'] == 'Peter'
|
||||
|
||||
All the other objects that are context bound can be used the same.
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ Bonus: Testing the Application
|
|||
===============================
|
||||
|
||||
Now that you have finished the application and everything works as
|
||||
expected, it's probably not the best idea to add automated tests to
|
||||
simplify modifications in the future. The application above is used as a
|
||||
basic example of how to perform unittesting in the :ref:`testing` section
|
||||
of the documentation. Go there to see how easy it is to test Flask
|
||||
applications.
|
||||
expected, it's probably not a good idea to add automated tests to simplify
|
||||
modifications in the future. The application above is used as a basic
|
||||
example of how to perform unittesting in the :ref:`testing` section of the
|
||||
documentation. Go there to see how easy it is to test Flask applications.
|
||||
|
|
|
|||
36
flask.py
36
flask.py
|
|
@ -13,6 +13,7 @@ from __future__ import with_statement
|
|||
import os
|
||||
import sys
|
||||
import types
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from itertools import chain
|
||||
from jinja2 import Environment, PackageLoader, FileSystemLoader
|
||||
|
|
@ -93,7 +94,20 @@ class _RequestGlobals(object):
|
|||
pass
|
||||
|
||||
|
||||
class _NullSession(SecureCookie):
|
||||
class Session(SecureCookie):
|
||||
"""Expands the session for support for switching between permanent
|
||||
and non-permanent sessions.
|
||||
"""
|
||||
|
||||
def _get_permanent(self):
|
||||
return self.get('_permanent', False)
|
||||
def _set_permanent(self, value):
|
||||
self['_permanent'] = bool(value)
|
||||
permanent = property(_get_permanent, _set_permanent)
|
||||
del _get_permanent, _set_permanent
|
||||
|
||||
|
||||
class _NullSession(Session):
|
||||
"""Class used to generate nicer error messages if sessions are not
|
||||
available. Will still allow read-only access to the empty session
|
||||
but fail on setting.
|
||||
|
|
@ -158,6 +172,11 @@ def url_for(endpoint, **values):
|
|||
any ``'admin.index'`` `index` of the `admin` module
|
||||
==================== ======================= =============================
|
||||
|
||||
Variable arguments that are unknown to the target endpoint are appended
|
||||
to the generated URL as query arguments.
|
||||
|
||||
For more information, head over to the :ref:`Quickstart <url-building>`.
|
||||
|
||||
:param endpoint: the endpoint of the URL (name of the function)
|
||||
:param values: the variable arguments of the URL rule
|
||||
"""
|
||||
|
|
@ -511,6 +530,11 @@ class Flask(_PackageBoundObject):
|
|||
#: The secure cookie uses this for the name of the session cookie
|
||||
session_cookie_name = 'session'
|
||||
|
||||
#: A :class:`~datetime.timedelta` which is used to set the expiration
|
||||
#: date of a permanent session. The default is 31 days which makes a
|
||||
#: permanent session survive for roughly one month.
|
||||
permanent_session_lifetime = timedelta(days=31)
|
||||
|
||||
#: options that are passed directly to the Jinja2 environment
|
||||
jinja_options = ImmutableDict(
|
||||
autoescape=True,
|
||||
|
|
@ -664,8 +688,8 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
key = self.secret_key
|
||||
if key is not None:
|
||||
return SecureCookie.load_cookie(request, self.session_cookie_name,
|
||||
secret_key=key)
|
||||
return Session.load_cookie(request, self.session_cookie_name,
|
||||
secret_key=key)
|
||||
|
||||
def save_session(self, session, response):
|
||||
"""Saves the session if it needs updates. For the default
|
||||
|
|
@ -676,7 +700,11 @@ class Flask(_PackageBoundObject):
|
|||
object)
|
||||
:param response: an instance of :attr:`response_class`
|
||||
"""
|
||||
session.save_cookie(response, self.session_cookie_name)
|
||||
expires = None
|
||||
if session.permanent:
|
||||
expires = datetime.utcnow() + self.permanent_session_lifetime
|
||||
session.save_cookie(response, self.session_cookie_name,
|
||||
expires=expires, httponly=True)
|
||||
|
||||
def register_module(self, module, **options):
|
||||
"""Registers a module with this application. The keyword argument
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@
|
|||
"""
|
||||
from __future__ import with_statement
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import flask
|
||||
import unittest
|
||||
import tempfile
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
from werkzeug import parse_date
|
||||
|
||||
|
||||
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
|
||||
|
|
@ -118,6 +121,30 @@ class BasicFunctionalityTestCase(unittest.TestCase):
|
|||
expect_exception(flask.session.__setitem__, 'foo', 42)
|
||||
expect_exception(flask.session.pop, 'foo')
|
||||
|
||||
def test_session_expiration(self):
|
||||
permanent = True
|
||||
app = flask.Flask(__name__)
|
||||
app.secret_key = 'testkey'
|
||||
@app.route('/')
|
||||
def index():
|
||||
flask.session['test'] = 42
|
||||
flask.session.permanent = permanent
|
||||
return ''
|
||||
rv = app.test_client().get('/')
|
||||
assert 'set-cookie' in rv.headers
|
||||
match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
|
||||
expires = parse_date(match.group())
|
||||
expected = datetime.utcnow() + app.permanent_session_lifetime
|
||||
assert expires.year == expected.year
|
||||
assert expires.month == expected.month
|
||||
assert expires.day == expected.day
|
||||
|
||||
permanent = False
|
||||
rv = app.test_client().get('/')
|
||||
assert 'set-cookie' in rv.headers
|
||||
match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
|
||||
assert match is None
|
||||
|
||||
def test_flashes(self):
|
||||
app = flask.Flask(__name__)
|
||||
app.secret_key = 'testkey'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue