Moved website into separate branch

This commit is contained in:
Armin Ronacher 2010-04-21 18:07:08 +02:00
parent e7811186e2
commit cdb17c55b3
97 changed files with 1 additions and 6930 deletions

2
.gitignore vendored
View file

@ -3,5 +3,5 @@
*.pyo
env
dist
website/_mailinglist/*
_mailinglist/*
*.egg-info

14
AUTHORS
View file

@ -1,14 +0,0 @@
Flask is written and maintained by Armin Ronacher and
various contributors:
Development Lead
````````````````
- Armin Ronacher <armin.ronacher@active-4.com>
Patches and Suggestions
```````````````````````
- Chris Edgemon
- Chris Grindstaff
- Florent Xicluna

21
CHANGES
View file

@ -1,21 +0,0 @@
Flask Changelog
===============
Here you can see the full list of changes between each Flask release.
Version 0.2
-----------
[unreleased; current development version]
- various bugfixes
- integrated JSON support
- added :func:`~flask.get_template_attribute` helper function.
- :meth:`~flask.Flask.add_url_rule` can now also register a
view function.
- server listens on 127.0.0.1 by default now to fix issues with chrome.
Version 0.1
-----------
First public preview release.

32
LICENSE
View file

@ -1,32 +0,0 @@
Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS
for more details.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,17 +0,0 @@
.PHONY: clean-pyc test
all: clean-pyc test
test:
python tests/flask_tests.py
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
upload-website:
scp -r website/* pocoo.org:/var/www/flask.pocoo.org/
upload-docs:
$(MAKE) -C docs dirhtml && scp -r docs/_build/dirhtml/* pocoo.org:/var/www/flask.pocoo.org/docs/

31
README
View file

@ -1,31 +0,0 @@
// Flask //
because sometimes a pocket knife is not enough
~ What is Flask?
Flask is a microframework for Python based on Werkzeug
and Jinja2. It's intended for small scale applications
and was developped with best intentions in mind.
~ Is it ready?
A preview release is out now, and I'm hoping for some
input about what you want from a microframework and
how it should look like. Consider the API to slightly
improve over time.
~ What do I need?
Jinja 2.4 and Werkzeug 0.6.1. `easy_install` will
install them for you if you do `easy_install Flask==dev`.
I encourage you to use a virtualenv. Check the docs for
complete installation and usage instructions.
~ Where are the docs?
Go to http://flask.pocoo.org/ for a prebuilt version of
the current documentation. Otherwise build them yourself
from the sphinx sources in the docs folder.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 90 KiB

1
docs/.gitignore vendored
View file

@ -1 +0,0 @@
_build

View file

@ -1,118 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Flask"
@echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
latexpdf: latex
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
@echo "Running LaTeX files through pdflatex..."
make -C _build/latex all-pdf
@echo "pdflatex finished; the PDF files are in _build/latex."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

BIN
docs/_static/flask.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,13 +0,0 @@
<h3>About Flask</h3>
<p>
Flask is a micro webdevelopment framework for Python. You are currently
looking at the documentation of the development version. Things are
not stable yet, but if you have some feedback,
<a href="mailto:armin.ronacher@active-4.com">let me know</a>.
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li>
<li><a href="http://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
<li><a href="http://github.com/mitsuhiko/flask">Flask @ github</a></li>
</ul>

View file

@ -1,3 +0,0 @@
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/flask.png', 1) }}" alt="Logo"/>
</a></p>

View file

@ -1,344 +0,0 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- flasky theme based on nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
background-color: #ddd;
color: #000;
margin: 0;
padding: 0;
}
div.document {
background: #fafafa;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
min-height: 34em;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
position: absolute;
right: 0;
margin-top: -70px;
text-align: right;
color: #888;
padding: 10px;
font-size: 14px;
}
div.footer a {
color: #888;
text-decoration: underline;
}
div.related {
line-height: 32px;
color: #888;
}
div.related ul {
padding: 0 0 0 10px;
}
div.related a {
color: #444;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 0 20px;
}
div.sphinxsidebarwrapper p.logo {
padding: 20px 0 10px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #222;
font-size: 24px;
font-weight: normal;
margin: 20px 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
}
div.sphinxsidebar a:hover {
text-decoration: underline;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body {
padding-bottom: 40px; /* saved for footer */
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 20px; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: white;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight{
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td {
padding: 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
}
a:hover tt {
background: #EEE;
}

View file

@ -1,3 +0,0 @@
[theme]
inherit = basic
stylesheet = flasky.css

View file

@ -1,262 +0,0 @@
.. _api:
API
===
.. module:: flask
This part of the documentation covers all the interfaces of Flask. For
parts where Flask depends on external libraries, we document the most
important right here and provide links to the canonical documentation.
Application Object
------------------
.. autoclass:: Flask
:members:
Incoming Request Data
---------------------
.. autoclass:: Request
.. 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 mimetype
the data is stored unmodified in this stream for consumption. Most
of the time it is a better idea to use :attr:`data` which will give
you that data as a string. The stream only returns the data once.
.. attribute:: data
Contains the incoming request data as string in case it came with
a mimetype Flask does not handle.
.. 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:: environ
The underlying WSGI environment.
.. 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``
`url_root` ``http://www.example.com/myapplication/``
============= ======================================================
.. attribute:: is_xhr
`True` if the request was triggered via a JavaScript
`XMLHttpRequest`. This only works with libraries that support the
``X-Requested-With`` header and set it to `XMLHttpRequest`.
Libraries that do that are prototype, jQuery and Mochikit and
probably some more.
.. attribute:: json
Contains the parsed body of the JSON request if the mimetype of
the incoming data was `application/json`. This requires Python 2.6
or an installed version of simplejson.
Response Objects
----------------
.. autoclass:: flask.Response
:members: set_cookie, data, mimetype
.. attribute:: headers
A :class:`Headers` object representing the response headers.
.. attribute:: status_code
The response status as integer.
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
Application Globals
-------------------
To share data that is valid for one request only from one function to
another, a global variable is not good enough because it would break in
threaded environments. Flask provides you with a special object that
ensures it is only valid for the active request and that will return
different values for each request. In a nutshell: it does the right
thing, like it does for :class:`request` and :class:`session`.
.. data:: g
Just store on this whatever you want. For example a database
connection or the user that is currently logged in.
Useful Functions and Classes
----------------------------
.. autofunction:: url_for
.. function:: abort(code)
Raises an :exc:`~werkzeug.exception.HTTPException` for the given
status code. For example to abort request handling with a page not
found exception, you would call ``abort(404)``.
:param code: the HTTP error code.
.. autofunction:: redirect
.. autofunction:: escape
.. autoclass:: Markup
:members: escape, unescape, striptags
Message Flashing
----------------
.. autofunction:: flash
.. autofunction:: get_flashed_messages
Returning JSON
--------------
.. autofunction:: jsonify
.. data:: json
If JSON support is picked up, this will be the module that Flask is
using to parse and serialize JSON. So instead of doing this yourself::
try:
import simplejson as json
except ImportError:
import json
You can instead just do this::
from flask import json
For usage examples, read the :mod:`json` documentation.
The :func:`~json.dumps` function of this json module is also available
as filter called ``|tojson`` in Jinja2. Note that inside `script`
tags no escaping must take place, so make sure to disable escaping
with ``|safe`` if you intend to use it inside `script` tags:
.. sourcecode:: html+jinja
<script type=text/javascript>
doSomethingWith({{ user.username|tojson|safe }});
</script>
Note that the ``|tojson`` filter escapes forward slashes properly.
Template Rendering
------------------
.. autofunction:: render_template
.. autofunction:: render_template_string
.. autofunction:: get_template_attribute

View file

@ -1,57 +0,0 @@
.. _becomingbig:
Becoming Big
============
Your application is becoming more and more complex? Flask is really not
designed for large scale applications and does not attempt to do so, but
that does not mean you picked the wrong tool in the first place.
Flask is powered by Werkzeug and Jinja2, two libraries that are in use at
a number of large websites out there and all Flask does is bring those
two together. Being a microframework, Flask is literally a single file.
What that means for large applications is that it's probably a good idea
to take the code from Flask and put it into a new module within the
applications and expand on that.
What Could Be Improved?
-----------------------
For instance it makes a lot of sense to change the way endpoints (the
names of the functions / URL rules) are handled to also take the module
name into account. Right now the function name is the URL name, but
imagine you have a large application consisting of multiple components.
In that case, it makes a lot of sense to use dotted names for the URL
endpoints.
Here are some suggestions for how Flask can be modified to better
accomodate large-scale applications:
- implement dotted names for URL endpoints
- get rid of the decorator function registering which causes a lot
of troubles for applications that have circular dependencies. It
also requires that the whole application is imported when the system
initializes or certain URLs will not be available right away. A
better solution would be to have one module with all URLs in there and
specifing the target functions explicitly or by name and importing
them when needed.
- switch to explicit request object passing. This requires more typing
(because you now have something to pass around) but it makes it a
whole lot easier to debug hairy situations and to test the code.
- integrate the `Babel`_ i18n package or `SQLAlchemy`_ directly into the
core framework.
.. _Babel: http://babel.edgewall.org/
.. _SQLAlchemy: http://www.sqlalchemy.org/
Why does Flask not do all that by Default?
------------------------------------------
There is a huge difference between a small application that only has to
handle a couple of requests per second and with an overall code complexity
of less than 4000 lines of code and something of larger scale. At some
point it becomes important to integrate external systems, different
storage backends and more.
If Flask was designed with all these contingencies in mind, it would be a
much more complex framework and harder to get started with.

View file

@ -1 +0,0 @@
.. include:: ../CHANGES

View file

@ -1,247 +0,0 @@
# -*- coding: utf-8 -*-
#
# Flask documentation build configuration file, created by
# sphinx-quickstart on Tue Apr 6 15:24:58 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Flask'
copyright = u'2010, Armin Ronacher'
import pkg_resources
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
release = __import__('pkg_resources').get_distribution('Flask').version
version = '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'flaskext.FlaskyStyle'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'flasky'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar. Do not set, template magic!
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'Flaskdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Flask.tex', u'Flask Documentation',
u'Armin Ronacher', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
#epub_title = ''
#epub_author = ''
#epub_publisher = ''
#epub_copyright = ''
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
intersphinx_mapping = {
'http://docs.python.org/dev': None,
'http://werkzeug.pocoo.org/documentation/dev/': None,
'http://www.sqlalchemy.org/docs/': None,
'http://wtforms.simplecodes.com/docs/0.5/': None
}

View file

@ -1,42 +0,0 @@
CGI
===
If all other deployment methods do not work, CGI will work for sure. CGI
is supported by all major servers but usually has a less-than-optimal
performance.
This is also the way you can use a Flask application on Google's
`AppEngine`_, there however the execution does happen in a CGI-like
environment. The application's performance is unaffected because of that.
.. _AppEngine: http://code.google.com/appengine/
Creating a `.cgi` file
----------------------
First you need to create the CGI application file. Let's call it
`yourapplication.cgi`::
#!/usr/bin/python
from wsgiref.handlers import CGIHandler
from yourapplication import app
CGIHandler().run(app)
If you're running Python 2.4 you will need the :mod:`wsgiref` package. Python
2.5 and higher ship this as part of the standard library.
Server Setup
------------
Usually there are two ways to configure the server. Either just copy the
`.cgi` into a `cgi-bin` (and use `mod_rerwite` or something similar to
rewrite the URL) or let the server point to the file directly.
In Apache for example you can put a like like this into the config:
.. sourcecode:: apache
ScriptAlias /app /path/to/the/application.cgi
For more information consult the documentation of your webserver.

View file

@ -1,128 +0,0 @@
FastCGI
=======
A very popular deployment setup on servers like `lighttpd`_ and `nginx`_
is FastCGI. To use your WSGI application with any of them you will need
a FastCGI server first.
The most popular one is `flup`_ which we will use for this guide. Make
sure to have it installed.
Creating a `.fcgi` file
-----------------------
First you need to create the FastCGI server file. Let's call it
`yourapplication.fcgi`::
#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from yourapplication import app
WSGIServer(app).run()
This is enough for Apache to work, however lighttpd and nginx need a
socket to communicate with the FastCGI server. For that to work you
need to pass the path to the socket to the
:class:`~flup.server.fcgi.WSGIServer`::
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
The path has to be the exact same path you define in the server
config.
Save the `yourapplication.fcgi` file somewhere you will find it again.
It makes sense to have that in `/var/www/yourapplication` or something
similar.
Make sure to set the executable bit on that file so that the servers
can execute it::
# chmod +x /var/www/yourapplication/yourapplication.fcgi
Configuring lighttpd
--------------------
A basic FastCGI configuration for lighttpd looks like that::
fastcgi.server = ("/yourapplication" =>
"yourapplication" => (
"socket" => "/tmp/yourapplication-fcgi.sock",
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
"check-local" => "disable"
)
)
This configuration binds the application to `/yourapplication`. If you
want the application to work in the URL root you have to work around a
lighttpd bug with the :class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix`
middleware.
Make sure to apply it only if you are mounting the application the URL
root.
Configuring nginx
-----------------
Installing FastCGI applications on nginx is a bit tricky because by default
some FastCGI parameters are not properly forwarded.
A basic FastCGI configuration for nginx looks like this::
location /yourapplication/ {
include fastcgi_params;
if ($uri ~ ^/yourapplication/(.*)?) {
set $path_url $1;
}
fastcgi_param PATH_INFO $path_url;
fastcgi_param SCRIPT_NAME /yourapplication;
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
This configuration binds the application to `/yourapplication`. If you want
to have it in the URL root it's a bit easier because you don't have to figure
out how to calculate `PATH_INFO` and `SCRIPT_NAME`::
location /yourapplication/ {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
Since Nginx doesn't load FastCGI apps, you have to do it by yourself. You
can either write an `init.d` script for that or execute it inside a screen
session::
$ screen
$ /var/www/yourapplication/yourapplication.fcgi
Debugging
---------
FastCGI deployments tend to be hard to debug on most webservers. Very often the
only thing the server log tells you is something along the lines of "premature
end of headers". In order to debug the application the only thing that can
really give you ideas why it breaks is switching to the correct user and
executing the application by hand.
This example assumes your application is called `application.fcgi` and that your
webserver user is `www-data`::
$ su www-data
$ cd /var/www/yourapplication
$ python application.fcgi
Traceback (most recent call last):
File "yourapplication.fcg", line 4, in <module>
ImportError: No module named yourapplication
In this case the error seems to be "yourapplication" not being on the python
path. Common problems are:
- relative paths being used. Don't rely on the current working directory
- the code depending on environment variables that are not set by the
web server.
- different python interpreters being used.
.. _lighttpd: http://www.lighttpd.net/
.. _nginx: http://nginx.net/
.. _flup: http://trac.saddi.com/flup

View file

@ -1,19 +0,0 @@
Deployment Options
==================
Depending on what you have available there are multiple ways to run Flask
applications. A very common method is to use the builtin server during
development and maybe behind a proxy for simple applications, but there
are more options available.
If you have a different WSGI server look up the server documentation about
how to use a WSGI app with it. Just remember that your application object
is the actual WSGI application.
.. toctree::
:maxdepth: 2
mod_wsgi
cgi
fastcgi
others

View file

@ -1,80 +0,0 @@
mod_wsgi (Apache)
=================
If you are using the `Apache`_ webserver you should consider using `mod_wsgi`_.
.. _Apache: http://httpd.apache.org/
Installing `mod_wsgi`
---------------------
If you don't have `mod_wsgi` installed yet you have to either install it using
a package manager or compile it yourself.
The mod_wsgi `installation instructions`_ cover source installations on UNIX
systems.
If you are using ubuntu / debian you can apt-get it and activate it as follows::
# apt-get install libapache2-mod-wsgi
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using
pkg_add::
# pkg_add -r mod_wsgi
If you are using pkgsrc you can install `mod_wsgi` by compiling the
`www/ap2-wsgi` package.
If you encounter segfaulting child processes after the first apache reload you
can safely ignore them. Just restart the server.
Creating a `.wsgi` file
-----------------------
To run your application you need a `yourapplication.wsgi` file. This file
contains the code `mod_wsgi` is executing on startup to get the application
object. The object called `application` in that file is then used as
application.
For most applications the following file should be sufficient::
from yourapplication import app as application
If you don't have a factory function for application creation but a singleton
instance you can directly import that one as `application`.
Store that file somewhere that you will find it again (e.g.:
`/var/www/yourapplication`) and make sure that `yourapplication` and all
the libraries that are in use are on the python load path. If you don't
want to install it system wide consider using a `virtual python`_ instance.
Configuring Apache
------------------
The last thing you have to do is to create an Apache configuration file for
your application. In this example we are telling `mod_wsgi` to execute the
application under a different user for security reasons:
.. sourcecode:: apache
<VirtualHost *>
ServerName example.com
WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
<Directory /var/www/yourapplication>
WSGIProcessGroup yourapplication
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
For more information consult the `mod_wsgi wiki`_.
.. _mod_wsgi: http://code.google.com/p/modwsgi/
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
.. _virtual python: http://pypi.python.org/pypi/virtualenv
.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/

View file

@ -1,48 +0,0 @@
Other Servers
=============
There are popular servers written in Python that allow the execution of
WSGI applications as well. Keep in mind though that some of these servers
were written for very specific applications and might not work as well for
standard WSGI application such as Flask powered ones.
Tornado
--------
`Tornado`_ is an open source version of the scalable, non-blocking web
server and tools that power `FriendFeed`_. Because it is non-blocking and
uses epoll, it can handle thousands of simultaneous standing connections,
which means it is ideal for real-time web services. Integrating this
service with Flask is a trivial task::
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from yourapplication import app
http_server = HTTPServer(WSGIContainer(app))
http_server.listen(5000)
IOLoop.instance().start()
.. _Tornado: http://www.tornadoweb.org/
.. _FriendFeed: http://friendfeed.com/
Gevent
-------
`Gevent`_ is a coroutine-based Python networking library that uses
`greenlet`_ to provide a high-level synchronous API on top of `libevent`_
event loop::
from gevent.wsgi import WSGIServer
from yourapplication import app
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
.. _Gevent: http://www.gevent.org/
.. _greenlet: http://codespeak.net/py/0.9.2/greenlet.html
.. _libevent: http://monkey.org/~provos/libevent/

View file

@ -1,147 +0,0 @@
Design Decisions in Flask
=========================
If you are curious why Flask does certain things the way it does and not
differently, this section is for you. This should give you an idea about
some of the design decisions that may appear arbitrary and surprising at
first, especially in direct comparison with other frameworks.
The Explicit Application Object
-------------------------------
A Python web application based on WSGI has to have one central callable
object that implements the actual application. In Flask this is an
instance of the :class:`~flask.Flask` class. Each Flask application has
to create an instance of this class itself and pass it the name of the
module, but why can't Flask do that itself?
Without such an explicit application object the following code::
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
Would look like this instead::
from hypothetical_flask import route
@route('/')
def index():
return 'Hello World!'
There are three major reasons for this. The most important one is that
implicit application objects require that there may only be one class at
the time. There are ways to fake multiple application with a single
application object, like maintaining a stack of applications, but this
causes some problems I won't outline here in detail. Now the question is:
when does a microframework need more than one application at the same
time? A good example for this is unittesting. When you want to test
something it can be very helpful to create a minimal application to test
specific behavior. When the application object is deleted everything it
allocated will be freed again.
Another thing that becomes possible when you have an explicit object laying
around in your code is that you can subclass the base class
(:class:`~flask.Flask`) to alter specific behaviour. This would not be
possible without hacks if the object were created ahead of time for you
based on a class that is not exposed to you.
But there is another very important reason why Flask depends on an
explicit instanciation of that class: the package name. Whenever you
create a Flask instance you usually pass it `__name__` as package name.
Flask depends on that information to properly load resources relative
to your module. With Python's outstanding support for reflection it can
then access the package to figure out where the templates and static files
are stored (see :meth:`~flask.Flask.open_resource`). Now obviously there
are frameworks around that do not need any configuration and will still be
able to load templates relative to your application module. But they have
to use the current working directory for that, which is a very unreliable
way to determine where the application is. The current working directory
is process-wide and if you are running multiple applications in one
process (which could happen in a webserver without you knowing) the paths
will be off. Worse: many webservers do not set the working directory to
the directory of your application but to the document root which does not
have to be the same folder.
The third reason is "explicit is better than implicit". That object is
your WSGI application, you don't have to remember anything else. If you
want to apply a WSGI middleware, just wrap it and you're done (though
there are better ways to do that so that you do not lose the reference
to the application object :meth:`~flask.Flask.wsgi_app`).
One Template Engine
-------------------
Flask decides on one template engine: Jinja2. Why doesn't Flask have a
pluggable template engine interface? You can obviously use a different
template engine, but Flask will still configure Jinja2 for you. While
that limitation that Jinja2 is *always* configured will probably go away,
the decision to bundle one template engine and use that will not.
Template engines are like programming languages and each of those engines
has a certain understanding about how things work. On the surface they
all work the same: you tell the engine to evaluate a template with a set
of variables and take the return value as string.
But that's about where similarities end. Jinja2 for example has an
extensive filter system, a certain way to do template inheritance, support
for reusable blocks (macros) that can be used from inside templates and
also from Python code, uses unicode for all operations, supports
iterative template rendering, configurable syntax and more. On the other
hand an engine like Genshi is based on XML stream evaluation, template
inheritance by taking the availability of XPath into account and more.
Mako on the other hand treats templates similar to Python modules.
When it comes to connecting a template engine with an application or
framework there is more than just rendering templates. For instance,
Flask uses Jinja2's extensive autoescaping support. Also it provides
ways to access macros from Jinja2 templates.
A template abstraction layer that would not take the unique features of
the template engines away is a science on its own and a too large
undertaking for a microframework like Flask.
Micro with Dependencies
-----------------------
Why does Flask call itself a microframework and yet it depends on two
libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look
over to the Ruby side of web development there we have a protocol very
similar to WSGI. Just that it's called Rack there, but besides that it
looks very much like a WSGI rendition for Ruby. But nearly all
applications in Ruby land do not work with Rack directly, but on top of a
library with the same name. This Rack library has two equivalents in
Python: WebOb (formerly Paste) and Werkzeug. Paste is still around but
from my understanding it's sort of deprecated in favour of WebOb. The
development of WebOb and Werkzeug started side by side with similar ideas
in mind: be a good implementation of WSGI for other applications to take
advantage.
Flask is a framework that takes advantage of the work already done by
Werkzeug to properly interface WSGI (which can be a complex task at
times). Thanks to recent developments in the Python package
infrastructure, packages with depencencies are no longer an issue and
there are very few reasons against having libraries that depend on others.
Thread Locals
-------------
Flask uses thread local objects (context local objects in fact, they
support greenlet contexts as well) for request, session and an extra
object you can put your own things on (:data:`~flask.g`). Why is that and
isn't that a bad idea?
Yes it is usually not such a bright idea to use thread locals. They cause
troubles for servers that are not based on the concept of threads and make
large applications harder to maintain. However Flask is just not designed
for large applications or asyncronous servers. Flask wants to make it
quick and easy to write a traditional web application.
Also see the :ref:`becomingbig` section of the documentation for some
inspiration for larger applications based on Flask.

View file

@ -1,86 +0,0 @@
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}

View file

@ -1,94 +0,0 @@
Foreword
========
Read this before you get started with Flask. This hopefully answers some
questions about the intention of the project, what it aims at and when you
should or should not be using it.
What does Micro Mean?
---------------------
The micro in microframework for me means on the one hand being small in
size and complexity but on the other hand also that the complexity of the
applications that are written with these frameworks do not exceed a
certain size. A microframework like Flask sacrifices a few things in
order to be approachable and to be as concise as possible.
For example Flask uses thread local objects internally so that you don't
have to pass objects around from function to function within a request in
order to stay threadsafe. While this is a really easy approach and saves
you a lot of time, it also does not scale well to large applications.
It's especially painful for more complex unittests and when you suddenly
have to deal with code being executed outside of the context of a request
(for example if you have cronjobs).
Flask provides some tools to deal with the downsides of this approach but
the core problem of this approach obviously stays. It is also based on
convention over configuration which means that a lot of things are
preconfigured in Flask and will work well for smaller applications but not
so much for larger ones (where and how it looks for templates, static
files etc.)
But don't worry if your application suddenly grows larger than it was
initially and you're afraid Flask might not grow with it. Even with
larger frameworks you sooner or later will find out that you need
something the framework just cannot do for you without modification.
If you are ever in that situation, check out the :ref:`becomingbig`
chapter.
A Framework and An Example
--------------------------
Flask is not only a microframework, it is also an example. Based on
Flask, there will be a series of blog posts that explain how to create a
framework. Flask itself is just one way to implement a framework on top
of existing libraries. Unlike many other microframeworks Flask does not
try to implement anything on its own, it reuses existing code.
Web Development is Dangerous
----------------------------
I'm not even joking. Well, maybe a little. If you write a web
application you are probably allowing users to register and leave their
data on your server. The users are entrusting you with data. And even if
you are the only user that might leave data in your application, you still
want that data to be stored in a secure manner.
Unfortunately there are many ways security of a web application can be
compromised. Flask protects you against one of the most common security
problems of modern web applications: cross site scripting (XSS). Unless
you deliberately mark insecure HTML as secure Flask (and the underlying
Jinja2 template engine) have you covered. But there are many more ways to
cause security problems.
Whenever something is dangerous where you have to watch out, the
documentation will tell you so. Some of the security concerns of web
development are far more complex than one might think and often we all end
up in situations where we think "well, this is just far fetched, how could
that possibly be exploited" and then an intelligent guy comes along and
figures a way out to exploit that application. And don't think, your
application is not important enough for hackers to take notice. Depending
ont he kind of attack, chances are there are automated botnets out there
trying to figure out how to fill your database with viagra adverisments.
So always keep that in mind when doing web development.
Target Audience
---------------
Is Flask for you? Is your application small-ish (less than 4000 lines of
Python code) and does not depend on too complex database structures, Flask
is the Framework for you. It was designed from the ground up to be easy
to use, based on established principles, good intentions and on top of two
established libraries in widespread usage.
Flask serves two purposes: it's an example of how to create a minimal and
opinionated framework on top of Werkzeug to show how this can be done, and
to provide people with a simple tool to prototype larger applications or
to implement small and medium sized applications.
If you suddenly discover that your application grows larger than
originally intended, head over to the :ref:`becomingbig` section to see
some possible solutions for larger applications.
Satisfied? Then head over to the :ref:`installation`.

View file

@ -1,68 +0,0 @@
Welcome to Flask
================
.. image:: _static/logo-full.png
:alt: The Flask Logo with Subtitle
:class: floatingflask
Welcome to Flask's documentation. This documentation is divided in
different parts. I would suggest to get started with the
:ref:`installation` and then heading over to the :ref:`quickstart`.
Besides the quickstart there is also a more detailed :ref:`tutorial` that
shows how to create a complete (albeit small) application with Flask. If
you rather want to dive into all the internal parts of Flask, check out
the :ref:`api` documentation. Common patterns are described in the
:ref:`patterns` section.
Flask also depends on two external libraries: the `Jinja2`_ template
engine and the `Werkzeug`_ WSGI toolkit. both of which are not documented
here. If you want to dive into their documentation check out the
following links:
- `Jinja2 Documentation <http://jinja.pocoo.org/2/documentation/>`_
- `Werkzeug Documentation <http://werkzeug.pocoo.org/documentation/>`_
.. _Jinja2: http://jinja.pocoo.org/2/
.. _Werkzeug: http://werkzeug.pocoo.org/
User's Guide
------------
This part of the documentation is written text and should give you an idea
how to work with Flask. It's a series of step-by-step instructions for
web development.
.. toctree::
:maxdepth: 2
foreword
installation
quickstart
tutorial/index
testing
patterns/index
deploying/index
becomingbig
Additional Notes
----------------
Design notes, legal information and changelog are here for the interested:
.. toctree::
:maxdepth: 2
design
license
changelog
API Reference
-------------
If you are looking for information on a specific function, class or
method, this part of the documentation is for you:
.. toctree::
:maxdepth: 2
api

View file

@ -1,169 +0,0 @@
.. _installation:
Installation
============
Flask is a microframework and yet it depends on external libraries. There
are various ways how you can install that library and this explains each
way and why there are multiple ways.
Flask depends on two external libraries: `Werkzeug
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/2/>`_.
The first on is responsible for interfacing WSGI the latter to render
templates. Now you are maybe asking, what is WSGI? WSGI is a standard
in Python that is basically responsible for ensuring that your application
is behaving in a specific way that you can run it on different
environments (for example on a local development server, on an Apache2, on
lighttpd, on Google's App Engine or whatever you have in mind).
So how do you get all that on your computer in no time? The most kick-ass
method is virtualenv, so let's look at that first.
virtualenv
----------
Virtualenv is what you want to use during development and in production if
you have shell access. So first: what does virtualenv do? If you are
like me and you like Python, chances are you want to use it for another
project as well. Now the more projects you have, the more likely it is
that you will be working with different versions of Python itself or a
library involved. Because let's face it: quite often libraries break
backwards compatibility and it's unlikely that your application will
not have any dependencies, that just won't happen. So virtualenv for the
rescue!
It basically makes it possible to have multiple side-by-side
"installations" of Python, each for your own project. It's not actually
an installation but a clever way to keep things separated.
So let's see how that works!
If you are on OS X or Linux chances are that one of the following two
commands will for for you::
$ sudo easy_install virtualenv
or even better::
$ sudo pip install virtualenv
Changes are you have virtualenv installed on your system then. Maybe it's
even in your package manager (on ubuntu try ``sudo apt-get install
python-virtualenv``).
If you are on Windows and missing the `easy_install` command you have to
install it first. Check the :ref:`windows-easy-install` section for more
information about how to do that. Once you have it installed, run the
same commands as above, but without the `sudo` part.
So now that you have virtualenv running just fire up a shell and create
your own environment. I usually create a folder and a `env` folder
within::
$ mkdir myproject
$ cd myproject
$ virtualenv env
New python executable in env/bin/python
Installing setuptools............done.
Now you only have to activate it, whenever you work with it. On OS X and
Linux do the following::
$ source env/bin/activate
If you are a Windows user, the following command is for you::
$ env\scripts\activate
Either way, you should now be using your virtualenv (see how the prompt of
your shell has changed to show the virtualenv).
Now you can just enter the following command to get Flask activated in
your virtualenv::
$ easy_install Flask
A few seconds later you are good to go.
System Wide Installation
------------------------
This is possible as well, but I would not recommend it. Just run
`easy_install` with root rights::
sudo easy_install Flask
(Run it in an Admin shell on Windows systems and without the `sudo`).
Leaving on the Edge
-------------------
You want to work with the latest version of Flask, there are two ways: you
can either let `easy_install` pull in the development version or tell it
to operate on a git checkout. Either way it's recommended to do that in a
virtualenv.
Get the git checkout in a new virtualenv and run in develop mode::
$ git clone http://github.com/mitsuhiko/flask.git
Initialized empty Git repository in ~/dev/flask/.git/
$ cd flask
$ virtualenv env
$ source env/bin/activate
New python executable in env/bin/python
Installing setuptools............done.
$ python setup.py develop
...
Finished processing dependencies for Flask
This will pull in the depdenencies and activate the git head as current
version. Then you just have to ``git pull origin`` to get the latest
version.
To just get the development version without git, do this instead::
$ mkdir flask
$ cd flask
$ virtualenv env
$ source env/bin/activate
New python executable in env/bin/python
Installing setuptools............done.
$ easy_install Flask==dev
...
Finished processing dependencies for Flask==dev
.. _windows-easy-install:
`easy_install` on Windows
-------------------------
On Windows installation of `easy_install` is a little bit tricker because
on Windows slightly different rules apply, but it's not a biggy. The
easiest way to accomplish that is downloading the `ez_setup.py`_ file and
running it. (Double clicking should do the trick)
Once you have done that it's important to add the `easy_install` command
and other Python scripts to the path. To do that you have to add the
Python installation's Script folder to the `PATH` variable.
To do that, click right on your "Computer" desktop icon and click
"Properties". On Windows Vista and Windows 7 then click on "Advanced System
settings", on Windows XP click on the "Advanced" tab instead. Then click
on the "Environment variables" button and double click on the "Path"
variable in the "System variables" section.
There append the path of your Python interpreter's Script folder to the
end of the last (make sure you delimit it from existing values with a
semicolon). Assuming you are using Python 2.6 on the default path, add
the following value::
;C:\Python26\Scripts
Then you are done. To check if it worked, open the cmd and execute
"easy_install". If you have UAC enabled it should prompt you for admin
privileges.
.. _ez_setup.py: http://peak.telecommunity.com/dist/ez_setup.py

View file

@ -1,21 +0,0 @@
License
=======
Flask is licensed under a three clause `BSD License`_. It basically
means: do whatever you want with it as long as the copyright in Flask
sticks around, the conditions are not modified and the disclaimer is
present. Furthermore you must not use the names of the authors to promote
derivates of the software without written consent.
.. _BSD License:
http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29
Authors
-------
.. include:: ../AUTHORS
License Text
------------
.. include:: ../LICENSE

View file

@ -1,139 +0,0 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Flask.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Flask.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% _build/devhelp
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

View file

@ -1,81 +0,0 @@
.. _message-flashing-pattern:
Message Flashing
================
Good applications and user interfaces are all about feedback. If the user
does not get enough feedback he 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.
So here a full example::
from flask import flash, redirect, url_for, render_template
@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 sucessfully logged in')
return redirect(url_for('index'))
return render_template('login.html', error=error)
And here the ``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 %}
And here the index.html template:
.. 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 of course the login template:
.. sourcecode:: html+jinja
{% extends "layout.html" %}
{% block body %}
<h1>Login</h1>
{% if error %}
<p class=error><strong>Error:</strong> {{ error }}
{% endif %}
<form action="" 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 %}

View file

@ -1,22 +0,0 @@
.. _patterns:
Patterns for Flask
==================
Certain things are common enough that the changes are high you will find
them in most web applications. For example quite a lot of applications
are using relational databases and user authentication. In that case,
changes are they will open a database connection at the beginning of the
request and get the information of the currently logged in user. At the
end of the request, the database connection is closed again.
.. toctree::
:maxdepth: 2
packages
sqlite3
sqlalchemy
wtforms
templateinheritance
flashing
jquery

View file

@ -1,167 +0,0 @@
AJAX with jQuery
================
`jQuery`_ is a small JavaScript library commonly used to simplify working
with the DOM and JavaScript in general. It is the perfect tool to make
web applications more dynamic by exchanging JSON between server and
client.
JSON itself is a very lightweight transport format, very similar to how
Python primitives (numbers, strings, dicts and lists) look like which is
widely supported and very easy to parse. It became popular a few years
ago and quickly replaced XML as transport format in web applications.
If you have Python 2.6 JSON will work out of the box, in Python 2.5 you
will have to install the `simplejson`_ library from PyPI.
.. _jQuery: http://jquery.com/
.. _simplejson: http://pypi.python.org/pypi/simplejson
Loading jQuery
--------------
In order to use jQuery, you have to download it first and place it in the
static folder of your application and then ensure it's loaded. Ideally
you have a layout template that is used for all pages where you just have
to add a script statement to your `head` to load jQuery:
.. sourcecode:: html
<script type=text/javascript src="{{
url_for('static', filename='jquery.js') }}"></script>
Another method is using Google's `AJAX Libraries API
<http://code.google.com/apis/ajaxlibs/documentation/>`_ to load jQuery:
.. sourcecode:: html
<script type=text/javascript
src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
In this case you don't have to put jQuery into your static folder, it will
instead be loaded from Google directly. This has the advantage that your
website will probably load faster for users if they were to at least one
other website before using the same jQuery version from Google because it
will already be in the browser cache. Downside is that if you don't have
network connectivity during development jQuery will not load.
Where is My Site?
-----------------
Do you know where your application is? If you are developing the answer
is quite simple: it's on localhost port something and directly on the root
of that server. But what if you later decide to move your application to
a different location? For example to ``http://example.com/myapp``? On
the server side this never was a problem because we were using the handy
:func:`~flask.url_for` function that did could answer that question for
us, but if we are using jQuery we should better not hardcode the path to
the application but make that dynamic, so how can we do that?
A simple method would be to add a script tag to our page that sets a
global variable to the prefix to the root of the application. Something
like this:
.. sourcecode:: html+jinja
<script type=text/javascript>
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
The ``|safe`` is necessary so that Jinja does not escape the JSON encoded
string with HTML rules. Usually this would be necessary, but we are
inside a `script` block here where different rules apply.
.. admonition:: Information for Pros
In HTML the `script` tag is declared `CDATA` which means that entities
will not be parsed. Everything until ``</script>`` is handled as script.
This also means that there must never be any ``</`` between the script
tags. ``|tojson`` is kindly enough to do the right thing here and
escape slashes for you (``{{ "</script>"|tojson|safe }`` is rendered as
``"<\/script>"``).
JSON View Functions
-------------------
Now let's create a server side function that accepts two URL arguments of
numbers which should be added together and then sent back to the
application in a JSON object. This is a really ridiculous example and is
something you usually would do on the client side alone, but a simple
example that shows how you would use jQuery and Flask nonetheless::
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)
@app.route('/_add_numbers')
def add_numbers():
a = request.args.get('a', 0, type=int)
b = request.args.get('b', 0, type=int)
return jsonify(result=a + b)
@app.route('/')
def index():
return render_template('index.html')
As you can see I also added an `index` method here that renders a
template. This template will load jQuery as above and have a little form
we can add two numbers and a link to trigger the function on the server
side.
Note that we are using the :meth:`~werkzeug.MultiDict.get` method here
which will never fail. If the key is missing a default value (here ``0``)
is returned. Furthermore it can convert values to a specific type (like
in our case `int`). This is especially handy for code that is
triggered by a script (APIs, JavaScript etc.) because you don't need
special error reporting in that case.
The HTML
--------
You index.html template either has to extend a `layout.html` template with
jQuery loaded and the `$SCRIPT_ROOT` variable set, or do that on the top.
Here the HTML code needed for our little application (`index.html`).
Notice that we also drop the script directly into the HTML here. It is
usually a better idea to have that in a separate script file:
.. sourcecode:: html
<script type=text/javascript>
$(function() {
$('a#calculate').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_add_numbers', {
a: $('input[name="a"]').val(),
b: $('input[name="b"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<h1>jQuery Example</h1>
<p><input type=text size=5 name=a> +
<input type=text size=5 name=b> =
<span id=result>?</span>
<p><a href=# id=calculate>calculate server side</a>
I won't got into detail here about how jQuery works, just a very quick
explanation of the little bit of code above:
1. ``$(function() { ... })`` specifies code that should run once the
browser is done loading the basic parts of the page.
2. ``#('selector')`` selects an element and lets you operate on it.
3. ``element.bind('event', func)`` specifies a function that should run
when the user clicked on the element. If that function returns
`false`, the default behaviour will not kick in (in this case, navigate
to the `#` URL).
4. ``$.getJSON(url, data, func)`` sends a `GET` request to `url` and will
send the contents of the `data` object as query parameters. Once the
data arrived, it will call the given function with the return value as
argument. Note that we can use the `$SCRIPT_ROOT` variable here that
we set earlier.
If you don't get the whole picture, download the `sourcecode
for this example
<http://github.com/mitsuhiko/flask/tree/master/examples/jqueryexample>`_
from github.

View file

@ -1,86 +0,0 @@
.. _larger-applications:
Larger Applications
===================
For larger applications it's a good idea to use a package instead of a
module. That is quite simple. Imagine a small application looks like
this::
/yourapplication
/yourapplication.py
/static
/style.css
/templates
layout.html
index.html
login.html
...
To convert that into a larger one, just create a new folder
`yourapplication` inside the existing one and move everything below it.
Then rename `yourapplication.py` to `__init__.py`. (Make sure to delete
all `.pyc` files first, otherwise things would most likely break)
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 `runserver.py` next to the inner
`yourapplication` folder with the following contents::
from yourapplication import app
app.run(debug=True)
What did we gain from this? Now we can restructure the application a bit
into multiple modules. The only thing you have to remember is the
following quick checklist:
1. the `Flask` application object creation has to be in the
`__init__.py` file. That way each module can import it safely and the
`__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 when in the `__init__.py` file.
Not the object itself, but the module it is in. Do the importing at
the *bottom* of the file.
Here an example `__init__.py`::
from flask import Flask
app = Flask(__name__)
import yourapplication.views
And this is what `views.py` would look like::
from yourapplication import app
@app.route('/')
def index():
return 'Hello World!'
.. 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 `views.py` depends on `__init__.py`). Be advised that this is a
bad idea in general but here it is actually fine. The reason for this is
that we are not actually using the views in `__init__.py` and just
ensuring the module is imported and we are doing that at the bottom of
the file.
There are still some problems with that approach but if you want to use
decorators there is no way around that. Check out the
:ref:`becomingbig` section for some inspiration how to deal with that.

View file

@ -1,193 +0,0 @@
.. _sqlalchemy-pattern:
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 (:ref:`larger-applications`).
While that is not necessary, it makes a lot of sense.
There are three very common ways to use SQLAlchemy. I will outline each
of them here:
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 the example `database.py` module for your application::
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import 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():
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 for you::
from yourapplication.database import db_session
@app.after_request
def shutdown_session(response):
db_session.remove()
return response
Here is an example model (put this into `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 '<User %r>' % (self.name, self.email)
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 u'admin'>]
>>> User.query.filter(User.name == 'admin').first()
<User u'admin'>
.. _SQLAlchemy: http://www.sqlalchemy.org/
.. _declarative:
http://www.sqlalchemy.org/docs/reference/ext/declarative.html
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 `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 for the declarative approach you need to close the session after
each request. Put this into your application module::
from yourapplication.database import db_session
@app.after_request
def shutdown_session(response):
db_session.remove()
return response
Here is an example table and model (put this into `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 '<User %r>' % (self.name, self.email)
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
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::
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, u'admin', u'admin@localhost')
These results are also dict-like tuples:
>>> r = users.select(users.c.id == 1).execute().first()
>>> r['name']
u'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, u'admin', u'admin@localhost')
For more information about SQLAlchemy, head over to the
`website <http://sqlalchemy.org/>`_.

View file

@ -1,87 +0,0 @@
.. _sqlite3:
Using SQLite 3 with Flask
=========================
In Flask you can implement opening of database connections at the beginning
of the request and closing at the end with the
:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request`
decorators in combination with the special :class:`~flask.g` object.
So here a simple example of how you can use SQLite 3 with Flask::
import sqlite3
from flask import g
DATABASE = '/path/to/database.db'
def connect_db():
return sqlite3.connect(DATABASE)
@app.before_request
def before_request():
g.db = connect_db()
@app.after_request
def after_request(response):
g.db.close()
return response
.. _easy-querying:
Easy Querying
-------------
Now in each request handling function you can access `g.db` to get the
current open database connection. To simplify working with SQLite a
helper function can be useful::
def query_db(query, args=(), one=False):
cur = g.db.execute(query, args)
rv = [dict((cur.description[idx][0], value)
for idx, value in enumerate(row)) for row in cur.fetchall()]
return (rv[0] if rv else None) if one else rv
This handy little function 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 formattings because this makes it possible
to attack the application using `SQL Injections
<http://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::
from contextlib import closing
def init_db():
with closing(connect_db()) as db:
with app.open_resource('schema.sql') 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()

View file

@ -1,69 +0,0 @@
.. _template-inheritance:
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 ``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 %}
&copy; Copyright 2010 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
In this example, the ``{% block %}`` tags define four blocks that child templates
can fill in. All the `block` tag does is to 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() }}``.

View file

@ -1,113 +0,0 @@
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
(:ref:`larger-applications`) for that and adding a separate module for the
forms.
The Forms
---------
This is an example form for a typical registration page::
from wtforms import Form, BooleanField, TextField, validators
class RegistrationForm(Form):
username = TextField('Username', [validators.Length(min=4, max=25)])
email = TextField('Email Address', [validators.Length(min=6, max=35)])
password = PasswordField('New Password', [validators.Required()])
confirm = PasswordField('Repeat Password', [validators.EqualTo(
'confirm', message='Passwords must match')])
accept_tos = BooleanField('I accept the TOS', [validators.Required()])
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')
redirect(url_for('login'))
return render_template('register.html', form=form)
Notice that we are implying that the view is using SQLAlchemy here
(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt
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 an example `_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 }}{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
This macro accepts a couple of keyword arguments that are forwarded to
WTForm's field function that renders the field for us. They keyword
arguments will be inserted as HTML attributes. So for example you can
call ``render_field(form.username, class='username')`` to add a class to
the input element. Note that WTForms returns standard Python unicode
strings, so we have to tell Jinja2 that this data is already HTML escaped
with the `|safe` filter.
Here the `register.html` template for the function we used above which
takes advantage of the `_formhelpers.html` template:
.. sourcecode:: html+jinja
{% from "_formhelpers.html" import render_field %}
<form method="post" action="/register">
<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: http://wtforms.simplecodes.com/
.. _WTForms website: http://wtforms.simplecodes.com/

View file

@ -1,618 +0,0 @@
.. _quickstart:
Quickstart
==========
Eager to get started? This page gives a good introduction in how to gets
started with Flask. This assumes you already have Flask installed. If
you do not, head over to the :ref:`installation` section.
A Minimal Application
---------------------
A minimal Flask application looks something like that::
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World!"
if __name__ == '__main__':
app.run()
Just save it as `hello.py` or something similar and run it with your
Python interpreter. Make sure to not call your application `flask.py`
because this would conflict with Flask itself.
::
$ python hello.py
* Running on http://127.0.0.1:5000/
Head over to `http://127.0.0.1:5000/ <http://127.0.0.1:5000/>`_, you should
see your hello world greeting.
So what did that code do?
1. first we imported the :class:`~flask.Flask` class. An instance of this
class will be our WSGI application.
2. next we create an instance of it. We pass it the name of the module /
package. This is needed so that Flask knows where it should look for
templates, static files and so on.
3. Then we use the :meth:`~flask.Flask.route` decorator to tell Flask
what URL should trigger our function.
4. The function then has a name which is also used to generate URLs to
that particular function, and returns the message we want to display in
the user's browser.
5. Finally we use the :meth:`~flask.Flask.run` function to run the
local server with our application. The ``if __name__ == '__main__':``
makes sure the server only runs if the script is executed directly from
the Python interpreter and not used as imported module.
To stop the server, hit control-C.
Debug Mode
----------
Now that :meth:`~flask.Flask.run` method is nice to start a local
development server, but you would have to restart it manually after each
change you do to code. That is not very nice and Flask can do better. If
you enable the debug support the server will reload itself on code changes
and also provide you with a helpful debugger if things go wrong.
There are two ways to enable debugging. Either set that flag on the
applciation object::
app.debug = True
app.run()
Or pass it to run::
app.run(debug=True)
Both will have exactly the same effect.
.. admonition:: Attention
The interactive debugger however does not work in forking environments
which makes it nearly impossible to use on production servers but the
debugger still allows the execution of arbitrary code which makes it a
major security risk and **must never be used on production machines**
because of that.
Screenshot of the debugger in action:
.. image:: _static/debugger.png
:align: center
:class: screenshot
:alt: screenshot of debugger in action
Routing
-------
As you have seen above, the :meth:`~flask.Flask.route` decorator is used
to bind a function to a URL. But there is more to it! You can make
certain parts of the URL dynamic and attach multiple rules to a function.
Here some examples::
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello World'
Variable Rules
``````````````
Modern web applications have beautiful URLs. This helps people remember
the URLs which is especially handy for applications that are used from
mobile devices with slower network connections. If the user can directly
go to the desired page without having to hit the index page it is more
likely he will like the page and come back next time.
To add variable parts to a URL you can mark these special sections as
``<variable_name>``. Such a part is then passed as keyword argument to
your function. Optionally a converter can be specifed by specifying a
rule with ``<converter:variable_name>``. Here some nice examples::
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
pass
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
pass
The following converters exist:
=========== ===========================================
`int` accepts integers
`float` like `int` but for floating point values
`path` like the default but also accepts slashes
=========== ===========================================
URL Building
````````````
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:
>>> from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route('/')
... def index(): pass
...
>>> @app.route('/login')
... def login(): pass
...
>>> @app.route('/user/<username>')
... def profile(username): pass
...
>>> with app.test_request_context():
... print url_for('index')
... print url_for('login')
... print url_for('profile', username='John Doe')
...
/
/login
/user/John%20Doe
(This also uses the :meth:`~flask.Flask.test_request_context` method
explained below. It basically tells flask to think we are handling a
request even though we are not, we are in an interactive Python shell.
Have a look at the explanation below. :ref:`context-locals`).
Why would you want to build URLs instead of hardcoding them in your
templates? There are three good reasons for this:
1. reversing is often more descriptive than hardcoding the URLs. Also and
more importantly you can change URLs in one go without having to change
the URLs all over the place.
2. URL building will handle escaping of special characters and unicode
data transparently for you, you don't have to deal with that.
3. If your application is placed outside the URL root (so say in
``/myapplication`` instead of ``/``), :func:`~flask.url_for` will
handle that properly for you.
HTTP Methods
````````````
HTTP (the protocol web applications are speaking) knows different methods
to access URLs. By default a route only answers to `GET` requests, but
that can be changed by providing the `methods` argument to the
:meth:`~flask.Flask.route` decorator. Here some examples::
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
do_the_login()
else:
show_the_login_form()
If `GET` is present, `HEAD` will be added automatically for you. You
don't have to deal with that. It will also make sure that `HEAD` requests
are handled like the `HTTP RFC`_ (the document describing the HTTP
protocol) demands, so you can completely ignore that part of the HTTP
specification.
You have no idea what an HTTP method is? Worry not, here quick
introduction in HTTP methods and why they matter:
The HTTP method (also often called "the verb") tells the server what the
clients wants to *do* with the requested page. The following methods are
very common:
`GET`
The Browser tells the server: just *get* me the information stored on
that page and send them to me. This is probably the most common
method.
`HEAD`
The Browser tells the server: get me the information, but I am only
interested in the *headers*, not the content of the page. An
application is supposed to handle that as if a `GET` request was
received but not deliver the actual contents. In Flask you don't have
to deal with that at all, the underlying Werkzeug library handles that
for you.
`POST`
The browser tells the server that it wants to *post* some new
information to that URL and that the server must ensure the data is
stored and only stored once. This is how HTML forms are usually
transmitting data to the server.
`PUT`
Similar to `POST` but the server might trigger the store procedure
multiple times by overwriting the old values more than once. Now you
might be asking why this is any useful, but there are some good
reasons to do that. Consider the connection is lost during
transmission, in that situation a system between the browser and the
server might sent the request safely a second time without breaking
things. With `POST` that would not be possible because it must only
be triggered once.
`DELETE`
Remove the information that the given location.
Now the interesting part is that in HTML4 and XHTML1, the only methods a
form might submit to the server are `GET` and `POST`. But with JavaScript
and future HTML standards you can use other methods as well. Furthermore
HTTP became quite popular lately and there are more things than browsers
that are speaking HTTP. (Your revision control system for instance might
speak HTTP)
.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt
Static Files
------------
Dynamic web applications need static files as well. That's usually where
the CSS and JavaScript files are coming from. Ideally your web server is
configured to serve them for you, but during development Flask can do that
as well. Just create a folder called `static` in your package or next to
your module and it will be available at `/static` on the application.
To generate URLs to that part of the URL, use the special ``'static'`` URL
name::
url_for('static', filename='style.css')
The file has to be stored on the filesystem as ``static/style.css``.
Rendering Templates
-------------------
Generating HTML from within Python is not fun, and actually pretty
cumbersome because you have to do the HTML escaping on your own to keep
the application secure. Because of that Flask configures the `Jinja2
<http://jinja.pocoo.org/2/>`_ template engine for you automatically.
To render a template you can use the :func:`~flask.render_template`
method. All you have to do is to provide the name of the template and the
variables you want to pass to the template engine as keyword arguments.
Here a simple example of how to render a template::
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
Flask will look for templates in the `templates` folder. So if your
application is a module, that folder is next to that module, if it's a
pacakge it's actually inside your package:
**Case 1**: a module::
/application.py
/templates
/hello.html
**Case 2**: a package::
/application
/__init__.py
/templates
/hello.html
For templates you can use the full power of Jinja2 templates. Head over
to the `Jinja2 Template Documentation
<http://jinja.pocoo.org/2/documentation/templates>`_ for more information.
Here an example template:
.. sourcecode:: html+jinja
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
Inside templates you also have access to the :class:`~flask.request`,
:class:`~flask.session` and :class:`~flask.g` [#]_ objects
as well as the :func:`~flask.get_flashed_messages` function.
Templates are especially useful if inheritance is used. If you want to
know how that works, head over to the :ref:`template-inheritance` pattern
documentation. Basically template inheritance makes it possible to keep
certain elements on each page (like header, navigation and footer).
Automatic escaping is enabled, so if name contains HTML it will be escaped
automatically. If you can trust a variable and you know that it will be
safe HTML (because for example it came from a module that converts wiki
markup to HTML) you can mark it as safe by using the
:class:`~jinja2.Markup` class or by using the ``|safe`` filter in the
template. Head over to the Jinja 2 documentation for more examples.
Here a basic introduction in how the :class:`~jinja2.Markup` class works:
>>> from flask import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
>>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
u'Marked up \xbb HTML'
.. [#] Unsure what that :class:`~flask.g` object is? It's something you
can store information on yourself, check the documentation of that
object (:class:`~flask.g`) and the :ref:`sqlite3` for more
information.
Accessing Request Data
----------------------
For web applications it's crucial to react to the data a client sent to
the server. In Flask this information is provided by the global
:class:`~flask.request` object. If you have some experience with Python
you might be wondering how that object can be global and how Flask
manages to still be threadsafe. The answer are context locals:
.. _context-locals:
Context Locals
``````````````
.. admonition:: Insider Information
If you want to understand how that works and how you can implement
tests with context locals, read this section, otherwise just skip it.
Certain objects in Flask are global objects, but not just a standard
global object, but actually a proxy to an object that is local to a
specific context. What a mouthful. But that is actually quite easy to
understand.
Imagine the context being the handling thread. A request comes in and the
webserver decides to spawn a new thread (or something else, the
underlying object is capable of dealing with other concurrency systems
than threads as well). When Flask starts its internal request handling it
figures out that the current thread is the active context and binds the
current application and the WSGI environments to that context (thread).
It does that in an intelligent way that one application can invoke another
application without breaking.
So what does this mean to you? Basically you can completely ignore that
this is the case unless you are unittesting or something different. You
will notice that code that depends on a request object will suddenly break
because there is no request object. The solution is creating a request
object yourself and binding it to the context. The easiest solution for
unittesting is by using the :meth:`~flask.Flask.test_request_context`
context manager. In combination with the `with` statement it will bind a
test request so that you can interact with it. Here an example::
from flask import request
with app.test_request_context('/hello', method='POST'):
# now you can do something with the request until the
# end of the with block, such as basic assertions:
assert request.path == '/hello'
assert request.method == 'POST'
The other possibility is passing a whole WSGI environment to the
:meth:`~flask.Flask.request_context` method::
from flask import request
with app.request_context(environ):
assert request.method == 'POST'
The Request Object
``````````````````
The request object is documented in the API section and we will not cover
it here in detail (see :class:`~flask.request`), but just mention some of
the most common operations. First of all you have to import it from the
the `flask` module::
from flask import request
The current request method is available by using the
:attr:`~flask.request.method` attribute. To access form data (data
transmitted in a `POST` or `PUT` request) you can use the
:attr:`~flask.request.form` attribute. Here a full example of the two
attributes mentioned above::
@app.route('/login', method=['POST', 'GET'])
def login():
error = None
if request.method == 'POST':
if valid_login(request.form['username'],
request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = 'Invalid username/password'
# this is executed if the request method was GET or the
# credentials were invalid
What happens if the key does not exist in the `form` attribute? In that
case a special :exc:`KeyError` is raised. You can catch it like a
standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request
error page is shown instead. So for many situations you don't have to
deal with that problem.
To access parameters submitted in the URL (``?key=value``) you can use the
:attr:`~flask.request.args` attribute::
searchword = request.args.get('q', '')
We recommend accessing URL parameters with `get` or by catching the
`KeyError` because users might change the URL and presenting them a 400
bad request page in that case is a bit user unfriendly.
For a full list of methods and attribtues on that object, head over to the
:class:`~flask.request` documentation.
File Uploads
````````````
Obviously you can handle uploaded files with Flask just as easy. Just
make sure not to forget to set the ``enctype="multipart/form-data"``
attribtue on your HTML form, otherwise the browser will not transmit your
files at all.
Uploaded files are stored in memory or at a temporary location on the
filesystem. You can access those files by looking at the
:attr:`~flask.request.files` attribute on the request object. Each
uploaded file is stored in that dictionary. It behaves just like a
standard Python :class:`file` object, but it also has a
:meth:`~werkzeug.FileStorage.save` method that allows you to store that
file on the filesystem of the server. Here a simple example how that
works::
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
...
If you want to know how the file was named on the client before it was
uploaded to your application, you can access the
:attr:`~werkzeug.FileStorage.filename` attribute. However please keep in
mind that this value can be forged so never ever trust that value. If you
want to use the filename of the client to store the file on the server,
pass it through the :func:`~werkzeug.secure_filename` function that
Werkzeug provides for you::
from flask import request
from werkzeug import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f= request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))
...
Cookies
```````
To access cookies you can use the :attr:`~flask.request.cookies`
attribute. Again this is a dictionary with all the cookies the client
transmits. If you want to use sessions, do not use the cookies directly
but instead use the :ref:`sessions` in Flask that add some security on top
of cookies for you.
Redirects and Errors
--------------------
To redirect a user to somewhere else you can use the
:func:`~flask.redirect` function, to abort a request early with an error
code the :func:`~flask.abort` function. Here an example how this works::
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
abort(401)
this_is_never_executed()
This is a rather pointless example because a user will be redirected from
the index to a page he cannot access (401 means access denied) but it
shows how that works.
By default a black and white error page is shown for each error code. If
you want to customize the error page, you can use the
:meth:`~flask.Flask.errorhandler` decorator::
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
Note the ``404`` after the :func:`~flask.render_template` call. This
tells Flask that the status code of that page should be 404 which means
not found. By default 200 is assumed which translats to: all went well.
.. _sessions:
Sessions
--------
Besides the request object there is also a second object called
:class:`~flask.session` that allows you to store information specific to a
user from one request to the next. This is implemented on top of cookies
for you and signes the cookies cryptographically. What this means is that
the user could look at the contents of your cookie but not modify it,
unless he knows the secret key used for signing.
In order to use sessions you have to set a secret key. Here is how
sessions work::
from flask import session, redirect, url_for, escape
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if its there
session.pop('username', None)
# set the secret key. keep this really secret:
app.secret_key = 'the secret key'
The here mentioned :func:`~flask.escape` does escaping for you if you are
not using the template engine (like in this example).
Message Flashing
----------------
Good applications and user interfaces are all about feedback. If the user
does not get enough feedback he 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.
To flash a message use the :func:`~flask.flash` method, to get hold of the
messages you can use :func:`~flask.get_flashed_messages` which is also
available in the templates. Check out the :ref:`message-flashing-pattern`
for a full example.

View file

@ -1,197 +0,0 @@
.. _testing:
Testing Flask Applications
==========================
**Something that is untested is broken.**
Not sure where that is coming from, and it's not entirely correct, but
also not that far from the truth. Untested applications make it hard to
improve existing code and developers of untested applications tend to
become pretty paranoid. If an application however has automated tests, you
can safely change things and you will instantly know if your change broke
something.
Flask gives you a couple of ways to test applications. It mainly does
that by exposing the Werkzeug test :class:`~werkzeug.Client` class to your
code and handling the context locals for you. You can then use that with
your favourite testing solution. In this documentation we will use the
:mod:`unittest` package that comes preinstalled with each Python
installation.
The Application
---------------
First we need an application to test for functionality. For the testing
we will use the application from the :ref:`tutorial`. If you don't have
that application yet, get the sources from `the examples`_.
.. _the examples:
http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
The Testing Skeleton
--------------------
In order to test that, we add a second module (
`flaskr_tests.py`) and create a unittest skeleton there::
import os
import flaskr
import unittest
import tempfile
class FlaskrTestCase(unittest.TestCase):
def setUp(self):
self.db_fd, flaskr.DATABASE = tempfile.mkstemp()
self.app = flaskr.app.test_client()
flaskr.init_db()
def tearDown(self):
os.close(self.db_fd)
os.unlink(flaskr.DATABASE)
if __name__ == '__main__':
unittest.main()
The code in the :meth:`~unittest.TestCase.setUp` method creates a new test
client and initializes a new database. That function is called before
each individual test function. To delete the database after the test, we
close the file and remove it from the filesystem in the
:meth:`~unittest.TestCase.tearDown` method. What the test client does is
give us a simple interface to the application. We can trigger test
requests to the application and the client will also keep track of cookies
for us.
Because SQLite3 is filesystem-based we can easily use the tempfile module
to create a temporary database and initialize it. The
:func:`~tempfile.mkstemp` function does two things for us: it returns a
low-level file handle and a random file name, the latter we use as
database name. We just have to keep the `db_fd` around so that we can use
the :func:`os.close` function to close the file.
If we now run that testsuite, we should see the following output::
$ python flaskr_tests.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Even though it did not run any tests, we already know that our flaskr
application is syntactically valid, otherwise the import would have died
with an exception.
The First Test
--------------
Now we can add the first test. Let's check that the application shows
"No entries here so far" if we access the root of the application (``/``).
For that we modify our created test case class so that it looks like
this::
class FlaskrTestCase(unittest.TestCase):
def setUp(self):
self.db_fd, flaskr.DATABASE = tempfile.mkstemp()
self.app = flaskr.app.test_client()
flaskr.init_db()
def tearDown(self):
os.close(self.db_fd)
os.unlink(flaskr.DATABASE)
def test_empty_db(self):
rv = self.app.get('/')
assert 'No entries here so far' in rv.data
Test functions begin with the word `test`. Every function named like that
will be picked up automatically. By using `self.app.get` we can send an
HTTP `GET` request to the application with the given path. The return
value will be a :class:`~flask.Flask.response_class` object. We can now
use the :attr:`~werkzeug.BaseResponse.data` attribute to inspect the
return value (as string) from the application. In this case, we ensure
that ``'No entries here so far'`` is part of the output.
Run it again and you should see one passing test::
$ python flaskr_tests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.034s
OK
Of course you can submit forms with the test client as well, which we will
use now to log our user in.
Logging In and Out
------------------
The majority of the functionality of our application is only available for
the administration user. So we need a way to log our test client in to the
application and out of it again. For that we fire some requests to the
login and logout pages with the required form data (username and
password). Because the login and logout pages redirect, we tell the
client to `follow_redirects`.
Add the following two methods to your `FlaskrTestCase` class::
def login(self, username, password):
return self.app.post('/login', data=dict(
username=username,
password=password
), follow_redirects=True)
def logout(self):
return self.app.get('/logout', follow_redirects=True)
Now we can easily test if logging in and out works and that it fails with
invalid credentials. Add this new test to the class::
def test_login_logout(self):
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD)
assert 'You were logged in' in rv.data
rv = self.logout()
assert 'You were logged out' in rv.data
rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD)
assert 'Invalid username' in rv.data
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x')
assert 'Invalid password' in rv.data
Test Adding Messages
--------------------
Now we can also test that adding messages works. Add a new test method
like this::
def test_messages(self):
self.login(flaskr.USERNAME, flaskr.PASSWORD)
rv = self.app.post('/add', data=dict(
title='<Hello>',
text='<strong>HTML</strong> allowed here'
), follow_redirects=True)
assert 'No entries here so far' not in rv.data
assert '&lt;Hello&gt' in rv.data
assert '<strong>HTML</strong> allowed here' in rv.data
Here we check that HTML is allowed in the text but not in the title,
which is the intended behavior.
Running that should now give us three passing tests::
$ python flaskr_tests.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.332s
OK
For more complex tests with headers and status codes, check out the
`MiniTwit Example`_ from the sources. That one contains a larger test
suite.
.. _MiniTwit Example:
http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/

View file

@ -1,27 +0,0 @@
Step 7: Adding Style
====================
Now that everything else works, it's time to add some style to the
application. Just create a stylesheet called `style.css` in the `static`
folder we created before:
.. sourcecode:: css
body { font-family: sans-serif; background: #eee; }
a, h1, h2 { color: #377BA8; }
h1, h2 { font-family: 'Georgia', serif; margin: 0; }
h1 { border-bottom: 2px solid #eee; }
h2 { font-size: 1.2em; }
.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
padding: 0.8em; background: white; }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
margin-bottom: 1em; background: #fafafa; }
.flash { background: #CEE5F5; padding: 0.5em;
border: 1px solid #AACBE2; }
.error { background: #F0D6D6; padding: 0.5em; }

View file

@ -1,33 +0,0 @@
Step 4: Request Database Connections
------------------------------------
Now we know how we can open database connections and use them for scripts,
but how can we elegantly do that for requests? We will need the database
connection in all our functions so it makes sense to initialize them
before each request and shut them down afterwards.
Flask allows us to do that with the :meth:`~flask.Flask.before_request` and
:meth:`~flask.Flask.after_request` decorators::
@app.before_request
def before_request():
g.db = connect_db()
@app.after_request
def after_request(response):
g.db.close()
return response
Functions marked with :meth:`~flask.Flask.before_request` are called before
a request and passed no arguments, functions marked with
:meth:`~flask.Flask.after_request` are called after a request and
passed the response that will be sent to the client. They have to return
that response object or a different one. In this case we just return it
unchanged.
We store our current database connection on the special :data:`~flask.g`
object that flask provides for us. This object stores information for one
request only and is available from within each function. Never store such
things on other objects because this would not work with threaded
environments. That special :data:`~flask.g` object does some magic behind
the scenes to ensure it does the right thing.

View file

@ -1,63 +0,0 @@
Step 3: Creating The Database
=============================
Flaskr is a database powered application as outlined earlier, and more
precisely, an application powered by a relational database system. Such
systems need a schema that tells them how to store that information. So
before starting the server for the first time it's important to create
that schema.
Such a schema can be created by piping the `schema.sql` file into the
`sqlite3` command as follows::
sqlite3 /tmp/flaskr.db < schema.sql
The downside of this is that it requires the sqlite3 command to be
installed which is not necessarily the case on every system. Also one has
to provide the path to the database there which leaves some place for
errors. It's a good idea to add a function that initializes the database
for you to the application.
If you want to do that, you first have to import the
:func:`contextlib.closing` function from the contextlib package. If you
want to use Python 2.5 it's also necessary to enable the `with` statement
first (`__future__` imports must be the very first import)::
from __future__ import with_statement
from contextlib import closing
Next we can create a function called `init_db` that initializes the
database. For this we can use the `connect_db` function we defined
earlier. Just add that function below the `connect_db` function::
def init_db():
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f:
db.cursor().executescript(f.read())
db.commit()
The :func:`~contextlib.closing` helper function allows us to keep a
connection open for the duration of the `with` block. The
:func:`~flask.Flask.open_resource` method of the application object
supports that functionality out of the box, so it can be used in the
`with` block directly. This function opens a file from the resource
location (your `flaskr` folder) and allows you to read from it. We are
using this here to execute a script on the database connection.
When we connect to a database we get a connection object (here called
`db`) that can give us a cursor. On that cursor there is a method to
execute a complete script. Finally we only have to commit the changes.
SQLite 3 and other transactional databases will not commit unless you
explicitly tell it to.
Now it is possible to create a database by starting up a Python shell and
importing and calling that function::
>>> from flaskr import init_db
>>> init_db()
.. admonition:: Troubleshooting
If you get an exception later that a table cannot be found check that
you did call the `init_db` function and that your table names are
correct (singular vs. plural for example).

View file

@ -1,19 +0,0 @@
Step 0: Creating The Folders
============================
Before we get started, let's create the folders needed for this
application::
/flaskr
/static
/templates
The `flaskr` folder is not a python package, but just something where we
drop our files. Directly into this folder we will then put our database
schema as well as main module in the following steps. The files inside
the `static` folder are available to users of the application via `HTTP`.
This is the place where css and javascript files go. Inside the
`templates` folder Flask will look for `Jinja2`_ templates. Drop all the
templates there.
.. _Jinja2: http://jinja.pocoo.org/2/

View file

@ -1,32 +0,0 @@
.. _tutorial:
Tutorial
========
You want to develop an application with Python and Flask? Here you have
the chance to learn that by example. In this tutorial we will create a
simple microblog application. It only supports one user that can create
text-only entries and there are no feeds or comments, but it still
features everything you need to get started. We will use Flask and SQLite
as database which comes out of the box with Python, so there is nothing
else you need.
If you want the full sourcecode in advance or for comparison, check out
the `example source`_.
.. _example source:
http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
.. toctree::
:maxdepth: 2
introduction
folders
schema
setup
dbinit
dbcon
views
templates
css
testing

View file

@ -1,29 +0,0 @@
Introducing Flaskr
==================
We will call our blogging application flaskr here, feel free to chose a
less web-2.0-ish name ;) Basically we want it to do the following things:
1. let the user sign in and out with credentials specified in the
configuration. Only one user is supported.
2. when the user is logged in he or she can add new entries to the page
consisting of a text-only title and some HTML for the text. This HTML
is not sanitized because we trust the user here.
3. the page shows all entries so far in reverse order (newest on top) and
the user can add new ones from there if logged in.
We will be using SQlite3 directly for that application because it's good
enough for an application of that size. For larger applications however
it makes a lot of sense to use `SQLAlchemy`_ that handles database
connections in a more intelligent way, allows you to target different
relational databases at once and more. You might also want to consider
one of the popular NoSQL databases if your data is more suited for those.
Here a screenshot from the final application:
.. image:: ../_static/flaskr.png
:align: center
:class: screenshot
:alt: screenshot of the final application
.. _SQLAlchemy: http://www.sqlalchemy.org/

View file

@ -1,21 +0,0 @@
Step 1: Database Schema
=======================
First we want to create the database schema. For this application only a
single table is needed and we only want to support SQLite so that is quite
easy. Just put the following contents into a file named `schema.sql` in
the just created `flaskr` folder:
.. sourcecode:: sql
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
This schema consists of a single table called `entries` and each row in
this table has an `id`, a `title` and a `text`. The `id` is an
automatically incrementing integer and a primary key, the other two are
strings that must not be null.

View file

@ -1,69 +0,0 @@
Step 2: Application Setup Code
==============================
Now that we have the schema in place we can create the application module.
Let's call it `flaskr.py` inside the `flaskr` folder. For starters we
will add the imports we will need as well as the config section. For
small applications it's a possibility to drop the configuration directly
into the module which we will be doing here. However a cleaner solution
would be to create a separate `.ini` or `.py` file and load that or import
the values from there.
::
# all the imports
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash
# configuration
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
Next we can create our actual application and initialize it with the
config::
# create our little application :)
app = Flask(__name__)
app.secret_key = SECRET_KEY
app.debug = DEBUG
The `secret_key` is needed to keep the client-side sessions secure.
Choose that key wisely and as hard to guess and complex as possible. The
debug flag enables or disables the interactive debugger. Never leave
debug mode activated in a production system because it will allow users to
executed code on the server!
We also add a method to easily connect to the database specified. That
can be used to open a connection on request and also from the interactive
Python shell or a script. This will come in handy later
::
def connect_db():
return sqlite3.connect(DATABASE)
Finally we just add a line to the bottom of the file that fires up the
server if we run that file as standalone application::
if __name__ == '__main__':
app.run()
With that out of the way you should be able to start up the application
without problems. When you head over to the server you will get an 404
page not found error because we don't have any views yet. But we will
focus on that a little later. First we should get the database working.
.. admonition:: Troubleshooting
If you notice later that the browser cannot connect to the server
during development, you might want to try this line instead::
app.run(host='127.0.0.1')
In a nutshell: Werkzeug starts up as IPv6 on many operating systems by
default and not every browser is happy with that. This forces IPv4
usage.

View file

@ -1,107 +0,0 @@
Step 6: The Templates
=====================
Now we should start working on the templates. If we request the URLs now
we would only get an exception that Flask cannot find the templates. The
templates are using `Jinja2`_ syntax and have autoescaping enabled by
default. This means that unless you mark a value in the code with
:class:`~flask.Markup` or with the ``|safe`` filter in the template,
Jinja2 will ensure that special characters such as ``<`` or ``>`` are
escaped with their XML equivalents.
We are also using template inheritance which makes it possible to reuse
the layout of the website in all pages.
Put the following templates into the `templates` folder:
.. _Jinja2: http://jinja.pocoo.org/2/documentation/templates
layout.html
-----------
This template contains the HTML skeleton, the header and a link to log in
(or log out if the user was already logged in). It also displays the
flashed messages if there are any. The ``{% block body %}`` block can be
replaced by a block of the same name (``body``) in a child template.
The :class:`~flask.session` dict is available in the template as well and
you can use that to check if the user is logged in or not. Note that in
Jinja you can access missing attributes and items of objects / dicts which
makes the following code work, even if there is no ``'logged_in'`` key in
the session:
.. sourcecode:: html+jinja
<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>Flaskr</h1>
<div class=metanav>
{% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a>
{% else %}
<a href="{{ url_for('logout') }}">log out</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
show_entries.html
-----------------
This template extends the `layout.html` template from above to display the
messages. Note that the `for` loop iterates over the messages we passed
in with the :func:`~flask.render_template` function. We also tell the
form to submit to your `add_entry` function and use `POST` as `HTTP`
method:
.. sourcecode:: html+jinja
{% extends "layout.html" %}
{% block body %}
{% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method=post class=add-entry>
<dl>
<dt>Title:
<dd><input type=text size=30 name=title>
<dt>Text:
<dd><textarea name=text rows=5 cols=40></textarea>
<dd><input type=submit value=Share>
</dl>
</form>
{% endif %}
<ul class=entries>
{% for entry in entries %}
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
{% else %}
<li><em>Unbelievable. No entries here so far</em>
{% endfor %}
</ul>
{% endblock %}
login.html
----------
Finally the login template which basically just displays a form to allow
the user to login:
.. sourcecode:: html+jinja
{% extends "layout.html" %}
{% block body %}
<h2>Login</h2>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username>
<dt>Password:
<dd><input type=password name=password>
<dd><input type=submit value=Login>
</dl>
</form>
{% endblock %}

View file

@ -1,9 +0,0 @@
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.

View file

@ -1,87 +0,0 @@
Step 5: The View Functions
==========================
Now that the database connections are working we can start writing the
view functions. We will need four of them:
Show Entries
------------
This view shows all the entries stored in the database. It listens on the
root of the application and will select title and text from the database.
The one with the highest id (the newest entry) on top. The rows returned
from the cursor are tuples with the columns ordered like specified in the
select statement. This is good enough for small applications like here,
but you might want to convert them into a dict. If you are interested how
to do that, check out the :ref:`easy-querying` example.
The view function will pass the entries as dicts to the
`show_entries.html` template and return the rendered one::
@app.route('/')
def show_entries():
cur = g.db.execute('select title, text from entries order by id desc')
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
return render_template('show_entries.html', entries=entries)
Add New Entry
-------------
This view lets the user add new entries if he's logged in. This only
responds to `POST` requests, the actual form is shown on the
`show_entries` page. If everything worked out well we will
:func:`~flask.flash` an information message to the next request and
redirect back to the `show_entries` page::
@app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('insert into entries (title, text) values (?, ?)',
[request.form['title'], request.form['text']])
g.db.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
Note that we check that the user is logged in here (the `logged_in` key is
present in the session and `True`).
Login and Logout
----------------
These functions are used to sign the user in and out. Login checks the
username and password against the ones from the configuration and sets the
`logged_in` key in the session. If the user logged in successfully that
key is set to `True` and the user is redirected back to the `show_entries`
page. In that case also a message is flashed that informs the user he or
she was logged in successfully. If an error occoured the template is
notified about that and the user asked again::
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != USERNAME:
error = 'Invalid username'
elif request.form['password'] != PASSWORD:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
The logout function on the other hand removes that key from the session
again. We use a neat trick here: if you use the :meth:`~dict.pop` method
of the dict and pass a second parameter to it (the default) the method
will delete the key from the dictionary if present or do nothing when that
key was not in there. This is helpful because we don't have to check in
that case if the user was logged in.
::
@app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('show_entries'))

View file

@ -1,26 +0,0 @@
/ Flaskr /
a minimal blog application
~ What is Flaskr?
A sqlite powered thumble blog application
~ How do I use it?
1. edit the configuration in the flaskr.py file
2. fire up a python shell and run this:
>>> from flaskr import init_db; init_db()
3. now you can run the flaskr.py file with your
python interpreter and the application will
greet you on http://localhost:5000/
~ Is it tested?
You betcha. Run the `flaskr_tests.py` file to see
the tests pass.

View file

@ -1,98 +0,0 @@
# -*- coding: utf-8 -*-
"""
Flaskr
~~~~~~
A microblog example application written as Flask tutorial with
Flask and sqlite3.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement
import sqlite3
from contextlib import closing
from flask import Flask, request, session, g, redirect, url_for, abort, \
render_template, flash
# configuration
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
# create our little application :)
app = Flask(__name__)
app.secret_key = SECRET_KEY
app.debug = DEBUG
def connect_db():
"""Returns a new connection to the database."""
return sqlite3.connect(DATABASE)
def init_db():
"""Creates the database tables."""
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f:
db.cursor().executescript(f.read())
db.commit()
@app.before_request
def before_request():
"""Make sure we are connected to the database each request."""
g.db = connect_db()
@app.after_request
def after_request(response):
"""Closes the database again at the end of the request."""
g.db.close()
return response
@app.route('/')
def show_entries():
cur = g.db.execute('select title, text from entries order by id desc')
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
return render_template('show_entries.html', entries=entries)
@app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('insert into entries (title, text) values (?, ?)',
[request.form['title'], request.form['text']])
g.db.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != USERNAME:
error = 'Invalid username'
elif request.form['password'] != PASSWORD:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
@app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('show_entries'))
if __name__ == '__main__':
app.run()

View file

@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
"""
Flaskr Tests
~~~~~~~~~~~~
Tests the Flaskr application.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
import os
import flaskr
import unittest
import tempfile
class FlaskrTestCase(unittest.TestCase):
def setUp(self):
"""Before each test, set up a blank database"""
self.db_fd, flaskr.DATABASE = tempfile.mkstemp()
self.app = flaskr.app.test_client()
flaskr.init_db()
def tearDown(self):
"""Get rid of the database again after each test."""
os.close(self.db_fd)
os.unlink(flaskr.DATABASE)
def login(self, username, password):
return self.app.post('/login', data=dict(
username=username,
password=password
), follow_redirects=True)
def logout(self):
return self.app.get('/logout', follow_redirects=True)
# testing functions
def test_empty_db(self):
"""Start with a blank database."""
rv = self.app.get('/')
assert 'No entries here so far' in rv.data
def test_login_logout(self):
"""Make sure login and logout works"""
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD)
assert 'You were logged in' in rv.data
rv = self.logout()
assert 'You were logged out' in rv.data
rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD)
assert 'Invalid username' in rv.data
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x')
assert 'Invalid password' in rv.data
def test_messages(self):
"""Test that messages work"""
self.login(flaskr.USERNAME, flaskr.PASSWORD)
rv = self.app.post('/add', data=dict(
title='<Hello>',
text='<strong>HTML</strong> allowed here'
), follow_redirects=True)
assert 'No entries here so far' not in rv.data
assert '&lt;Hello&gt' in rv.data
assert '<strong>HTML</strong> allowed here' in rv.data
if __name__ == '__main__':
unittest.main()

View file

@ -1,6 +0,0 @@
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title string not null,
text string not null
);

View file

@ -1,18 +0,0 @@
body { font-family: sans-serif; background: #eee; }
a, h1, h2 { color: #377BA8; }
h1, h2 { font-family: 'Georgia', serif; margin: 0; }
h1 { border-bottom: 2px solid #eee; }
h2 { font-size: 1.2em; }
.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
padding: 0.8em; background: white; }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
margin-bottom: 1em; background: #fafafa; }
.flash { background: #CEE5F5; padding: 0.5em;
border: 1px solid #AACBE2; }
.error { background: #F0D6D6; padding: 0.5em; }

View file

@ -1,17 +0,0 @@
<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>Flaskr</h1>
<div class=metanav>
{% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a>
{% else %}
<a href="{{ url_for('logout') }}">log out</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>

View file

@ -1,14 +0,0 @@
{% extends "layout.html" %}
{% block body %}
<h2>Login</h2>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username>
<dt>Password:
<dd><input type=password name=password>
<dd><input type=submit value=Login>
</dl>
</form>
{% endblock %}

View file

@ -1,21 +0,0 @@
{% extends "layout.html" %}
{% block body %}
{% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method=post class=add-entry>
<dl>
<dt>Title:
<dd><input type=text size=30 name=title>
<dt>Text:
<dd><textarea name=text rows=5 cols=40></textarea>
<dd><input type=submit value=Share>
</dl>
</form>
{% endif %}
<ul class=entries>
{% for entry in entries %}
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
{% else %}
<li><em>Unbelievable. No entries here so far</em>
{% endfor %}
</ul>
{% endblock %}

View file

@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
"""
jQuery Example
~~~~~~~~~~~~~~
A simple application that shows how Flask and jQuery get along.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)
@app.route('/_add_numbers')
def add_numbers():
"""Add two numbers server side, ridiculous but well..."""
a = request.args.get('a', 0, type=int)
b = request.args.get('b', 0, type=int)
return jsonify(result=a + b)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()

View file

@ -1,21 +0,0 @@
{% extends "layout.html" %}
{% block body %}
<script type=text/javascript>
$(function() {
$('a#calculate').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_add_numbers', {
a: $('input[name="a"]').val(),
b: $('input[name="b"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<h1>jQuery Example</h1>
<p><input type=text size=5 name=a> +
<input type=text size=5 name=b> =
<span id=result>?</span>
<p><a href=# id=calculate>calculate server side</a>
{% endblock %}

View file

@ -1,10 +0,0 @@
<!doctype html>
<title>jQuery Example</title>
<script type=text/javascript
src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type=text/javascript src="{{ url_for('static', filename='app.js')
}}"></script>
<script type=text/javascript>
var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
{% block body %}{% endblock %}

View file

@ -1,26 +0,0 @@
/ MiniTwit /
because writing todo lists is not fun
~ What is MiniTwit?
A SQLite and Flask powered twitter clone
~ How do I use it?
1. edit the configuration in the minitwit.py file
2. fire up a python shell and run this:
>>> from minitwit import init_db; init_db()
3. now you can run the minitwit.py file with your
python interpreter and the application will
greet you on http://localhost:5000/
~ Is it tested?
You betcha. Run the `minitwit_tests.py` file to
see the tests pass.

View file

@ -1,249 +0,0 @@
# -*- coding: utf-8 -*-
"""
MiniTwit
~~~~~~~~
A microblogging application written with Flask and sqlite3.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement
import time
import sqlite3
from hashlib import md5
from datetime import datetime
from contextlib import closing
from flask import Flask, request, session, url_for, redirect, \
render_template, abort, g, flash
from werkzeug import check_password_hash, generate_password_hash
# configuration
DATABASE = '/tmp/minitwit.db'
PER_PAGE = 30
DEBUG = True
SECRET_KEY = 'development key'
# create our little application :)
app = Flask(__name__)
def connect_db():
"""Returns a new connection to the database."""
return sqlite3.connect(DATABASE)
def init_db():
"""Creates the database tables."""
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f:
db.cursor().executescript(f.read())
db.commit()
def query_db(query, args=(), one=False):
"""Queries the database and returns a list of dictionaries."""
cur = g.db.execute(query, args)
rv = [dict((cur.description[idx][0], value)
for idx, value in enumerate(row)) for row in cur.fetchall()]
return (rv[0] if rv else None) if one else rv
def get_user_id(username):
"""Convenience method to look up the id for a username."""
rv = g.db.execute('select user_id from user where username = ?',
[username]).fetchone()
return rv[0] if rv else None
def format_datetime(timestamp):
"""Format a timestamp for display."""
return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d @ %H:%M')
def gravatar_url(email, size=80):
"""Return the gravatar image for the given email address."""
return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \
(md5(email.strip().lower().encode('utf-8')).hexdigest(), size)
@app.before_request
def before_request():
"""Make sure we are connected to the database each request and look
up the current user so that we know he's there.
"""
g.db = connect_db()
g.user = None
if 'user_id' in session:
g.user = query_db('select * from user where user_id = ?',
[session['user_id']], one=True)
@app.after_request
def after_request(response):
"""Closes the database again at the end of the request."""
g.db.close()
return response
@app.route('/')
def timeline():
"""Shows a users timeline or if no user is logged in it will
redirect to the public timeline. This timeline shows the user's
messages as well as all the messages of followed users.
"""
if not g.user:
return redirect(url_for('public_timeline'))
return render_template('timeline.html', messages=query_db('''
select message.*, user.* from message, user
where message.author_id = user.user_id and (
user.user_id = ? or
user.user_id in (select whom_id from follower
where who_id = ?))
order by message.pub_date desc limit ?''',
[session['user_id'], session['user_id'], PER_PAGE]))
@app.route('/public')
def public_timeline():
"""Displays the latest messages of all users."""
return render_template('timeline.html', messages=query_db('''
select message.*, user.* from message, user
where message.author_id = user.user_id
order by message.pub_date desc limit ?''', [PER_PAGE]))
@app.route('/<username>')
def user_timeline(username):
"""Display's a users tweets."""
profile_user = query_db('select * from user where username = ?',
[username], one=True)
if profile_user is None:
abort(404)
followed = False
if g.user:
followed = query_db('''select 1 from follower where
follower.who_id = ? and follower.whom_id = ?''',
[session['user_id'], profile_user['user_id']],
one=True) is not None
return render_template('timeline.html', messages=query_db('''
select message.*, user.* from message, user where
user.user_id = message.author_id and user.user_id = ?
order by message.pub_date desc limit ?''',
[profile_user['user_id'], PER_PAGE]), followed=followed,
profile_user=profile_user)
@app.route('/<username>/follow')
def follow_user(username):
"""Adds the current user as follower of the given user."""
if not g.user:
abort(401)
whom_id = get_user_id(username)
if whom_id is None:
abort(404)
g.db.execute('insert into follower (who_id, whom_id) values (?, ?)',
[session['user_id'], whom_id])
g.db.commit()
flash('You are now following "%s"' % username)
return redirect(url_for('user_timeline', username=username))
@app.route('/<username>/unfollow')
def unfollow_user(username):
"""Removes the current user as follower of the given user."""
if not g.user:
abort(401)
whom_id = get_user_id(username)
if whom_id is None:
abort(404)
g.db.execute('delete from follower where who_id=? and whom_id=?',
[session['user_id'], whom_id])
g.db.commit()
flash('You are no longer following "%s"' % username)
return redirect(url_for('user_timeline', username=username))
@app.route('/add_message', methods=['POST'])
def add_message():
"""Registers a new message for the user."""
if 'user_id' not in session:
abort(401)
if request.form['text']:
g.db.execute('''insert into message (author_id, text, pub_date)
values (?, ?, ?)''', (session['user_id'], request.form['text'],
int(time.time())))
g.db.commit()
flash('Your message was recorded')
return redirect(url_for('timeline'))
@app.route('/login', methods=['GET', 'POST'])
def login():
"""Logs the user in."""
if g.user:
return redirect(url_for('timeline'))
error = None
if request.method == 'POST':
user = query_db('''select * from user where
username = ?''', [request.form['username']], one=True)
if user is None:
error = 'Invalid username'
elif not check_password_hash(user['pw_hash'],
request.form['password']):
error = 'Invalid password'
else:
flash('You were logged in')
session['user_id'] = user['user_id']
return redirect(url_for('timeline'))
return render_template('login.html', error=error)
@app.route('/register', methods=['GET', 'POST'])
def register():
"""Registers the user."""
if g.user:
return redirect(url_for('timeline'))
error = None
if request.method == 'POST':
if not request.form['username']:
error = 'You have to enter a username'
elif not request.form['email'] or \
'@' not in request.form['email']:
error = 'You have to enter a valid email address'
elif not request.form['password']:
error = 'You have to enter a password'
elif request.form['password'] != request.form['password2']:
error = 'The two passwords do not match'
elif get_user_id(request.form['username']) is not None:
error = 'The username is already taken'
else:
g.db.execute('''insert into user (
username, email, pw_hash) values (?, ?, ?)''',
[request.form['username'], request.form['email'],
generate_password_hash(request.form['password'])])
g.db.commit()
flash('You were successfully registered and can login now')
return redirect(url_for('login'))
return render_template('register.html', error=error)
@app.route('/logout')
def logout():
"""Logs the user out."""
flash('You were logged out')
session.pop('user_id', None)
return redirect(url_for('public_timeline'))
# add some filters to jinja and set the secret key and debug mode
# from the configuration.
app.jinja_env.filters['datetimeformat'] = format_datetime
app.jinja_env.filters['gravatar'] = gravatar_url
app.secret_key = SECRET_KEY
app.debug = DEBUG
if __name__ == '__main__':
app.run()

View file

@ -1,149 +0,0 @@
# -*- coding: utf-8 -*-
"""
MiniTwit Tests
~~~~~~~~~~~~~~
Tests the MiniTwit application.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
import os
import minitwit
import unittest
import tempfile
class MiniTwitTestCase(unittest.TestCase):
def setUp(self):
"""Before each test, set up a blank database"""
self.db_fd, minitwit.DATABASE = tempfile.mkstemp()
self.app = minitwit.app.test_client()
minitwit.init_db()
def tearDown(self):
"""Get rid of the database again after each test."""
os.close(self.db_fd)
os.unlink(minitwit.DATABASE)
# helper functions
def register(self, username, password, password2=None, email=None):
"""Helper function to register a user"""
if password2 is None:
password2 = password
if email is None:
email = username + '@example.com'
return self.app.post('/register', data={
'username': username,
'password': password,
'password2': password2,
'email': email,
}, follow_redirects=True)
def login(self, username, password):
"""Helper function to login"""
return self.app.post('/login', data={
'username': username,
'password': password
}, follow_redirects=True)
def register_and_login(self, username, password):
"""Registers and logs in in one go"""
self.register(username, password)
return self.login(username, password)
def logout(self):
"""Helper function to logout"""
return self.app.get('/logout', follow_redirects=True)
def add_message(self, text):
"""Records a message"""
rv = self.app.post('/add_message', data={'text': text},
follow_redirects=True)
if text:
assert 'Your message was recorded' in rv.data
return rv
# testing functions
def test_register(self):
"""Make sure registering works"""
rv = self.register('user1', 'default')
assert 'You were successfully registered ' \
'and can login now' in rv.data
rv = self.register('user1', 'default')
assert 'The username is already taken' in rv.data
rv = self.register('', 'default')
assert 'You have to enter a username' in rv.data
rv = self.register('meh', '')
assert 'You have to enter a password' in rv.data
rv = self.register('meh', 'x', 'y')
assert 'The two passwords do not match' in rv.data
rv = self.register('meh', 'foo', email='broken')
assert 'You have to enter a valid email address' in rv.data
def test_login_logout(self):
"""Make sure logging in and logging out works"""
rv = self.register_and_login('user1', 'default')
assert 'You were logged in' in rv.data
rv = self.logout()
assert 'You were logged out' in rv.data
rv = self.login('user1', 'wrongpassword')
assert 'Invalid password' in rv.data
rv = self.login('user2', 'wrongpassword')
assert 'Invalid username' in rv.data
def test_message_recording(self):
"""Check if adding messages works"""
self.register_and_login('foo', 'default')
self.add_message('test message 1')
self.add_message('<test message 2>')
rv = self.app.get('/')
assert 'test message 1' in rv.data
assert '&lt;test message 2&gt;' in rv.data
def test_timelines(self):
"""Make sure that timelines work"""
self.register_and_login('foo', 'default')
self.add_message('the message by foo')
self.logout()
self.register_and_login('bar', 'default')
self.add_message('the message by bar')
rv = self.app.get('/public')
assert 'the message by foo' in rv.data
assert 'the message by bar' in rv.data
# bar's timeline should just show bar's message
rv = self.app.get('/')
assert 'the message by foo' not in rv.data
assert 'the message by bar' in rv.data
# now let's follow foo
rv = self.app.get('/foo/follow', follow_redirects=True)
assert 'You are now following &#34;foo&#34;' in rv.data
# we should now see foo's message
rv = self.app.get('/')
assert 'the message by foo' in rv.data
assert 'the message by bar' in rv.data
# but on the user's page we only want the user's message
rv = self.app.get('/bar')
assert 'the message by foo' not in rv.data
assert 'the message by bar' in rv.data
rv = self.app.get('/foo')
assert 'the message by foo' in rv.data
assert 'the message by bar' not in rv.data
# now unfollow and check if that worked
rv = self.app.get('/foo/unfollow', follow_redirects=True)
assert 'You are no longer following &#34;foo&#34;' in rv.data
rv = self.app.get('/')
assert 'the message by foo' not in rv.data
assert 'the message by bar' in rv.data
if __name__ == '__main__':
unittest.main()

View file

@ -1,21 +0,0 @@
drop table if exists user;
create table user (
user_id integer primary key autoincrement,
username string not null,
email string not null,
pw_hash string not null
);
drop table if exists follower;
create table follower (
who_id integer,
whom_id integer
);
drop table if exists message;
create table message (
message_id integer primary key autoincrement,
author_id integer not null,
text string not null,
pub_date integer
);

View file

@ -1,178 +0,0 @@
body {
background: #CAECE9;
font-family: 'Trebuchet MS', sans-serif;
font-size: 14px;
}
a {
color: #26776F;
}
a:hover {
color: #333;
}
input[type="text"],
input[type="password"] {
background: white;
border: 1px solid #BFE6E2;
padding: 2px;
font-family: 'Trebuchet MS', sans-serif;
font-size: 14px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
color: #105751;
}
input[type="submit"] {
background: #105751;
border: 1px solid #073B36;
padding: 1px 3px;
font-family: 'Trebuchet MS', sans-serif;
font-size: 14px;
font-weight: bold;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
color: white;
}
div.page {
background: white;
border: 1px solid #6ECCC4;
width: 700px;
margin: 30px auto;
}
div.page h1 {
background: #6ECCC4;
margin: 0;
padding: 10px 14px;
color: white;
letter-spacing: 1px;
text-shadow: 0 0 3px #24776F;
font-weight: normal;
}
div.page div.navigation {
background: #DEE9E8;
padding: 4px 10px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #eee;
color: #888;
font-size: 12px;
letter-spacing: 0.5px;
}
div.page div.navigation a {
color: #444;
font-weight: bold;
}
div.page h2 {
margin: 0 0 15px 0;
color: #105751;
text-shadow: 0 1px 2px #ccc;
}
div.page div.body {
padding: 10px;
}
div.page div.footer {
background: #eee;
color: #888;
padding: 5px 10px;
font-size: 12px;
}
div.page div.followstatus {
border: 1px solid #ccc;
background: #E3EBEA;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
padding: 3px;
font-size: 13px;
}
div.page ul.messages {
list-style: none;
margin: 0;
padding: 0;
}
div.page ul.messages li {
margin: 10px 0;
padding: 5px;
background: #F0FAF9;
border: 1px solid #DBF3F1;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
min-height: 48px;
}
div.page ul.messages p {
margin: 0;
}
div.page ul.messages li img {
float: left;
padding: 0 10px 0 0;
}
div.page ul.messages li small {
font-size: 0.9em;
color: #888;
}
div.page div.twitbox {
margin: 10px 0;
padding: 5px;
background: #F0FAF9;
border: 1px solid #94E2DA;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
div.page div.twitbox h3 {
margin: 0;
font-size: 1em;
color: #2C7E76;
}
div.page div.twitbox p {
margin: 0;
}
div.page div.twitbox input[type="text"] {
width: 585px;
}
div.page div.twitbox input[type="submit"] {
width: 70px;
margin-left: 5px;
}
ul.flashes {
list-style: none;
margin: 10px 10px 0 10px;
padding: 0;
}
ul.flashes li {
background: #B9F3ED;
border: 1px solid #81CEC6;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
padding: 4px;
font-size: 13px;
}
div.error {
margin: 10px 0;
background: #FAE4E4;
border: 1px solid #DD6F6F;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
padding: 4px;
font-size: 13px;
}

View file

@ -1,32 +0,0 @@
<!doctype html>
<title>{% block title %}Welcome{% endblock %} | MiniTwit</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>MiniTwit</h1>
<div class=navigation>
{% if g.user %}
<a href="{{ url_for('timeline') }}">my timeline</a> |
<a href="{{ url_for('public_timeline') }}">public timeline</a> |
<a href="{{ url_for('logout') }}">sign out [{{ g.user.username }}]</a>
{% else %}
<a href="{{ url_for('public_timeline') }}">public timeline</a> |
<a href="{{ url_for('register') }}">sign up</a> |
<a href="{{ url_for('login') }}">sign in</a>
{% endif %}
</div>
{% with flashes = get_flashed_messages() %}
{% if flashes %}
<ul class=flashes>
{% for message in flashes %}
<li>{{ message }}
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<div class=body>
{% block body %}{% endblock %}
</div>
<div class=footer>
MiniTwit &mdash; A Flask Application
</div>
</div>

View file

@ -1,16 +0,0 @@
{% extends "layout.html" %}
{% block title %}Sign In{% endblock %}
{% block body %}
<h2>Sign In</h2>
{% if error %}<div class=error><strong>Error:</strong> {{ error }}</div>{% endif %}
<form action="" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username size=30 value="{{ request.form.username }}">
<dt>Password:
<dd><input type=password name=password size=30>
</dl>
<div class=actions><input type=submit value="Sign In"></div>
</form>
{% endblock %}

View file

@ -1,19 +0,0 @@
{% extends "layout.html" %}
{% block title %}Sign Up{% endblock %}
{% block body %}
<h2>Sign Up</h2>
{% if error %}<div class=error><strong>Error:</strong> {{ error }}</div>{% endif %}
<form action="" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username size=30 value="{{ request.form.username }}">
<dt>E-Mail:
<dd><input type=text name=email size=30 value="{{ request.form.email }}">
<dt>Password:
<dd><input type=password name=password size=30>
<dt>Password <small>(repeat)</small>:
<dd><input type=password name=password2 size=30>
</dl>
<div class=actions><input type=submit value="Sign Up"></div>
</form>
{% endblock %}

View file

@ -1,49 +0,0 @@
{% extends "layout.html" %}
{% block title %}
{% if request.endpoint == 'public_timeline' %}
Public Timeline
{% elif request.endpoint == 'user_timeline' %}
{{ profile_user.username }}'s Timeline
{% else %}
My Timeline
{% endif %}
{% endblock %}
{% block body %}
<h2>{{ self.title() }}</h2>
{% if g.user %}
{% if request.endpoint == 'user_timeline' %}
<div class=followstatus>
{% if g.user.user_id == profile_user.user_id %}
This is you!
{% elif followed %}
You are currently following this user.
<a class=unfollow href="{{ url_for('unfollow_user', username=profile_user.username)
}}">Unfollow user</a>.
{% else %}
You are not yet following this user.
<a class=follow href="{{ url_for('follow_user', username=profile_user.username)
}}">Follow user</a>.
{% endif %}
</div>
{% elif request.endpoint == 'timeline' %}
<div class=twitbox>
<h3>What's on your mind {{ g.user.username }}?</h3>
<form action="{{ url_for('add_message') }}" method=post>
<p><input type=text name=text size=60><!--
--><input type=submit value="Share">
</form>
</div>
{% endif %}
{% endif %}
<ul class=messages>
{% for message in messages %}
<li><img src="{{ message.email|gravatar(size=48) }}"><p>
<strong><a href="{{ url_for('user_timeline', username=message.username)
}}">{{ message.username }}</a></strong>
{{ message.text }}
<small>&mdash; {{ message.pub_date|datetimeformat }}</small>
{% else %}
<li><em>There's no message so far.</em>
{% endfor %}
</ul>
{% endblock %}

801
flask.py
View file

@ -1,801 +0,0 @@
# -*- coding: utf-8 -*-
"""
flask
~~~~~
A microframework based on Werkzeug. It's extensively documented
and follows best practice patterns.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement
import os
import sys
from jinja2 import Environment, PackageLoader, FileSystemLoader
from werkzeug import Request as RequestBase, Response as ResponseBase, \
LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
ImmutableDict, cached_property
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException
from werkzeug.contrib.securecookie import SecureCookie
# try to load the best simplejson implementation available. If JSON
# is not installed, we add a failing class.
json_available = True
try:
import simplejson as json
except ImportError:
try:
import json
except ImportError:
json_available = False
# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.
from werkzeug import abort, redirect
from jinja2 import Markup, escape
# use pkg_resource if that works, otherwise fall back to cwd. The
# current working directory is generally not reliable with the notable
# exception of google appengine.
try:
import pkg_resources
pkg_resources.resource_stream
except (ImportError, AttributeError):
pkg_resources = None
class Request(RequestBase):
"""The request object used by default in flask. Remembers the
matched endpoint and view arguments.
It is what ends up as :class:`~flask.request`. If you want to replace
the request object used you can subclass this and set
:attr:`~flask.Flask.request_class` to your subclass.
"""
endpoint = view_args = None
@cached_property
def json(self):
"""If the mimetype is `application/json` this will contain the
parsed JSON data.
"""
if __debug__:
_assert_have_json()
if self.mimetype == 'application/json':
return json.loads(self.data)
class Response(ResponseBase):
"""The response object that is used by default in flask. Works like the
response object from Werkzeug but is set to have a HTML mimetype by
default. Quite often you don't have to create this object yourself because
:meth:`~flask.Flask.make_response` will take care of that for you.
If you want to replace the response object used you can subclass this and
set :attr:`~flask.Flask.request_class` to your subclass.
"""
default_mimetype = 'text/html'
class _RequestGlobals(object):
pass
class _NullSession(SecureCookie):
"""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.
"""
def _fail(self, *args, **kwargs):
raise RuntimeError('the session is unavailable because no secret '
'key was set. Set the secret_key on the '
'application to something unique and secret')
__setitem__ = __delitem__ = clear = pop = popitem = \
update = setdefault = _fail
del _fail
class _RequestContext(object):
"""The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the
`_request_ctx_stack` and removed at the end of it. It will create the
URL adapter and request object for the WSGI environment provided.
"""
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
if self.session is None:
self.session = _NullSession()
self.g = _RequestGlobals()
self.flashes = None
def __enter__(self):
_request_ctx_stack.push(self)
def __exit__(self, exc_type, exc_value, tb):
# do not pop the request stack if we are in debug mode and an
# exception happened. This will allow the debugger to still
# access the request object in the interactive shell.
if tb is None or not self.app.debug:
_request_ctx_stack.pop()
def url_for(endpoint, **values):
"""Generates a URL to the given endpoint with the method provided.
:param endpoint: the endpoint of the URL (name of the function)
:param values: the variable arguments of the URL rule
"""
return _request_ctx_stack.top.url_adapter.build(endpoint, values)
def get_template_attribute(template_name, attribute):
"""Loads a macro (or variable) a template exports. This can be used to
invoke a macro from within Python code. If you for example have a
template named `_foo.html` with the following contents:
.. sourcecode:: html+jinja
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
You can access this from Python code like this::
hello = get_template_attribute('_foo.html', 'hello')
return hello('World')
.. versionadded:: 0.2
:param template_name: the name of the template
:param attribute: the name of the variable of macro to acccess
"""
return getattr(current_app.jinja_env.get_template(template_name).module,
attribute)
def flash(message):
"""Flashes a message to the next request. In order to remove the
flashed message from the session and to display it to the user,
the template has to call :func:`get_flashed_messages`.
:param message: the message to be flashed.
"""
session.setdefault('_flashes', []).append(message)
def get_flashed_messages():
"""Pulls all flashed messages from the session and returns them.
Further calls in the same request to the function will return
the same messages.
"""
flashes = _request_ctx_stack.top.flashes
if flashes is None:
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
return flashes
def jsonify(*args, **kwargs):
"""Creates a :class:`~flask.Response` with the JSON representation of
the given arguments with an `application/json` mimetype. The arguments
to this function are the same as to the :class:`dict` constructor.
Example usage::
@app.route('/_get_current_user')
def get_current_user():
return jsonify(username=g.user.username,
email=g.user.email,
id=g.user.id)
This will send a JSON response like this to the browser::
{
"username": "admin",
"email": "admin@localhost",
"id": 42
}
This requires Python 2.6 or an installed version of simplejson.
.. versionadded:: 0.2
"""
if __debug__:
_assert_have_json()
return current_app.response_class(json.dumps(dict(*args, **kwargs),
indent=None if request.is_xhr else 2), mimetype='application/json')
def render_template(template_name, **context):
"""Renders a template from the template folder with the given
context.
:param template_name: the name of the template to be rendered
:param context: the variables that should be available in the
context of the template.
"""
current_app.update_template_context(context)
return current_app.jinja_env.get_template(template_name).render(context)
def render_template_string(source, **context):
"""Renders a template from the given template source string
with the given context.
:param template_name: the sourcecode of the template to be
rendered
:param context: the variables that should be available in the
context of the template.
"""
current_app.update_template_context(context)
return current_app.jinja_env.from_string(source).render(context)
def _default_template_ctx_processor():
"""Default template context processor. Injects `request`,
`session` and `g`.
"""
reqctx = _request_ctx_stack.top
return dict(
request=reqctx.request,
session=reqctx.session,
g=reqctx.g
)
def _assert_have_json():
"""Helper function that fails if JSON is unavailable."""
if not json_available:
raise RuntimeError('simplejson not installed')
def _get_package_path(name):
"""Returns the path to a package or cwd if that cannot be found."""
try:
return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
except (KeyError, AttributeError):
return os.getcwd()
# figure out if simplejson escapes slashes. This behaviour was changed
# from one version to another without reason.
if not json_available or '\\/' not in json.dumps('/'):
def _tojson_filter(*args, **kwargs):
if __debug__:
_assert_have_json()
return json.dumps(*args, **kwargs).replace('/', '\\/')
else:
_tojson_filter = json.dumps
class Flask(object):
"""The flask object implements a WSGI application and acts as the central
object. It is passed the name of the module or package of the
application. Once it is created it will act as a central registry for
the view functions, the URL rules, template configuration and much more.
The name of the package is used to resolve resources from inside the
package or the folder the module is contained in depending on if the
package parameter resolves to an actual python package (a folder with
an `__init__.py` file inside) or a standard module (just a `.py` file).
For more information about resource loading, see :func:`open_resource`.
Usually you create a :class:`Flask` instance in your main module or
in the `__init__.py` file of your package like this::
from flask import Flask
app = Flask(__name__)
"""
#: the class that is used for request objects. See :class:`~flask.request`
#: for more information.
request_class = Request
#: the class that is used for response objects. See
#: :class:`~flask.Response` for more information.
response_class = Response
#: path for the static files. If you don't want to use static files
#: you can set this value to `None` in which case no URL rule is added
#: and the development server will no longer serve any static files.
static_path = '/static'
#: if a secret key is set, cryptographic components can use this to
#: sign cookies and other things. Set this to a complex random value
#: when you want to use the secure cookie for instance.
secret_key = None
#: The secure cookie uses this for the name of the session cookie
session_cookie_name = 'session'
#: options that are passed directly to the Jinja2 environment
jinja_options = ImmutableDict(
autoescape=True,
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
)
def __init__(self, package_name):
#: the debug flag. Set this to `True` to enable debugging of
#: the application. In debug mode the debugger will kick in
#: when an unhandled exception ocurrs and the integrated server
#: will automatically reload the application if changes in the
#: code are detected.
self.debug = False
#: the name of the package or module. Do not change this once
#: it was set by the constructor.
self.package_name = package_name
#: where is the app root located?
self.root_path = _get_package_path(self.package_name)
#: a dictionary of all view functions registered. The keys will
#: be function names which are also used to generate URLs and
#: the values are the function objects themselves.
#: to register a view function, use the :meth:`route` decorator.
self.view_functions = {}
#: a dictionary of all registered error handlers. The key is
#: be the error code as integer, the value the function that
#: should handle that error.
#: To register a error handler, use the :meth:`errorhandler`
#: decorator.
self.error_handlers = {}
#: a list of functions that should be called at the beginning
#: of the request before request dispatching kicks in. This
#: can for example be used to open database connections or
#: getting hold of the currently logged in user.
#: To register a function here, use the :meth:`before_request`
#: decorator.
self.before_request_funcs = []
#: a list of functions that are called at the end of the
#: request. The function is passed the current response
#: object and modify it in place or replace it.
#: To register a function here use the :meth:`after_request`
#: decorator.
self.after_request_funcs = []
#: a list of functions that are called without arguments
#: to populate the template context. Each returns a dictionary
#: that the template context is updated with.
#: To register a function here, use the :meth:`context_processor`
#: decorator.
self.template_context_processors = [_default_template_ctx_processor]
#: the :class:`~werkzeug.routing.Map` for this instance. You can use
#: this to change the routing converters after the class was created
#: but before any routes are connected. Example::
#:
#: from werkzeug import BaseConverter
#:
#: class ListConverter(BaseConverter):
#: def to_python(self, value):
#: return value.split(',')
#: def to_url(self, values):
#: return ','.join(BaseConverter.to_url(value)
#: for value in values)
#:
#: app = Flask(__name__)
#: app.url_map.converters['list'] = ListConverter
self.url_map = Map()
if self.static_path is not None:
self.add_url_rule(self.static_path + '/<filename>',
build_only=True, endpoint='static')
if pkg_resources is not None:
target = (self.package_name, 'static')
else:
target = os.path.join(self.root_path, 'static')
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
self.static_path: target
})
#: the Jinja2 environment. It is created from the
#: :attr:`jinja_options` and the loader that is returned
#: by the :meth:`create_jinja_loader` function.
self.jinja_env = Environment(loader=self.create_jinja_loader(),
**self.jinja_options)
self.jinja_env.globals.update(
url_for=url_for,
get_flashed_messages=get_flashed_messages
)
self.jinja_env.filters['tojson'] = _tojson_filter
def create_jinja_loader(self):
"""Creates the Jinja loader. By default just a package loader for
the configured package is returned that looks up templates in the
`templates` folder. To add other loaders it's possible to
override this method.
"""
if pkg_resources is None:
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
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.
:param context: the context as a dictionary that is updated in place
to add extra variables.
"""
for func in self.template_context_processors:
context.update(func())
def run(self, host='127.0.0.1', port=5000, **options):
"""Runs the application on a local development server. If the
:attr:`debug` flag is set the server will automatically reload
for code changes and show a debugger in case an exception happened.
:param host: the hostname to listen on. set this to ``'0.0.0.0'``
to have the server available externally as well.
:param port: the port of the webserver
:param options: the options to be forwarded to the underlying
Werkzeug server. See :func:`werkzeug.run_simple`
for more information.
"""
from werkzeug import run_simple
if 'debug' in options:
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options)
def test_client(self):
"""Creates a test client for this application. For information
about unit testing head over to :ref:`testing`.
"""
from werkzeug import Client
return Client(self, self.response_class, use_cookies=True)
def open_resource(self, resource):
"""Opens a resource from the application's resource folder. To see
how this works, consider the following folder structure::
/myapplication.py
/schemal.sql
/static
/style.css
/template
/layout.html
/index.html
If you want to open the `schema.sql` file you would do the
following::
with app.open_resource('schema.sql') as f:
contents = f.read()
do_something_with(contents)
:param resource: the name of the resource. To access resources within
subfolders use forward slashes as separator.
"""
if pkg_resources is None:
return open(os.path.join(self.root_path, resource), 'rb')
return pkg_resources.resource_stream(self.package_name, resource)
def open_session(self, request):
"""Creates or opens a new session. Default implementation stores all
session data in a signed cookie. This requires that the
:attr:`secret_key` is set.
:param request: an instance of :attr:`request_class`.
"""
key = self.secret_key
if key is not None:
return SecureCookie.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
implementation, check :meth:`open_session`.
:param session: the session to be saved (a
:class:`~werkzeug.contrib.securecookie.SecureCookie`
object)
:param response: an instance of :attr:`response_class`
"""
session.save_cookie(response, self.session_cookie_name)
def add_url_rule(self, rule, endpoint, view_func=None, **options):
"""Connects a URL rule. Works exactly like the :meth:`route`
decorator. If a view_func is provided it will be registered with the
endpoint.
Basically this example::
@app.route('/')
def index():
pass
Is equivalent to the following::
def index():
pass
app.add_url_rule('/', 'index', index)
If the view_func is not provided you will need to connect the endpoint
to a view function like so::
app.view_functions['index'] = index
.. versionchanged:: 0.2
`view_func` parameter added
:param rule: the URL rule as string
:param endpoint: the endpoint for the registered URL rule. Flask
itself assumes the name of the view function as
endpoint
:param view_func: the function to call when serving a request to the
provided endpoint
:param options: the options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object
"""
options['endpoint'] = endpoint
options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))
if view_func is not None:
self.view_functions[endpoint] = view_func
def route(self, rule, **options):
"""A decorator that is used to register a view function for a
given URL rule. Example::
@app.route('/')
def index():
return 'Hello World'
Variables parts in the route can be specified with angular
brackets (``/user/<username>``). By default a variable part
in the URL accepts any string without a slash however a different
converter can be specified as well by using ``<converter:name>``.
Variable parts are passed to the view function as keyword
arguments.
The following converters are possible:
=========== ===========================================
`int` accepts integers
`float` like `int` but for floating point values
`path` like the default but also accepts slashes
=========== ===========================================
Here some examples::
@app.route('/')
def index():
pass
@app.route('/<username>')
def show_user(username):
pass
@app.route('/post/<int:post_id>')
def show_post(post_id):
pass
An important detail to keep in mind is how Flask deals with trailing
slashes. The idea is to keep each URL unique so the following rules
apply:
1. If a rule ends with a slash and is requested without a slash
by the user, the user is automatically redirected to the same
page with a trailing slash attached.
2. If a rule does not end with a trailing slash and the user request
the page with a trailing slash, a 404 not found is raised.
This is consistent with how web servers deal with static files. This
also makes it possible to use relative link targets safely.
The :meth:`route` decorator accepts a couple of other arguments
as well:
:param rule: the URL rule as string
:param methods: a list of methods this rule should be limited
to (``GET``, ``POST`` etc.). By default a rule
just listens for ``GET`` (and implicitly ``HEAD``).
:param subdomain: specifies the rule for the subdoain in case
subdomain matching is in use.
:param strict_slashes: can be used to disable the strict slashes
setting for this rule. See above.
:param options: other options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object.
"""
def decorator(f):
self.add_url_rule(rule, f.__name__, f, **options)
return f
return decorator
def errorhandler(self, code):
"""A decorator that is used to register a function give a given
error code. Example::
@app.errorhandler(404)
def page_not_found():
return 'This page does not exist', 404
You can also register a function as error handler without using
the :meth:`errorhandler` decorator. The following example is
equivalent to the one above::
def page_not_found():
return 'This page does not exist', 404
app.error_handlers[404] = page_not_found
:param code: the code as integer for the handler
"""
def decorator(f):
self.error_handlers[code] = f
return f
return decorator
def before_request(self, f):
"""Registers a function to run before each request."""
self.before_request_funcs.append(f)
return f
def after_request(self, f):
"""Register a function to be run after each request."""
self.after_request_funcs.append(f)
return f
def context_processor(self, f):
"""Registers a template context processor function."""
self.template_context_processors.append(f)
return f
def match_request(self):
"""Matches the current request against the URL map and also
stores the endpoint and view arguments on the request object
is successful, otherwise the exception is stored.
"""
rv = _request_ctx_stack.top.url_adapter.match()
request.endpoint, request.view_args = rv
return rv
def dispatch_request(self):
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
proper response object, call :func:`make_response`.
"""
try:
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
except HTTPException, e:
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception, e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
def make_response(self, rv):
"""Converts the return value from a view function to a real
response object that is an instance of :attr:`response_class`.
The following types are allowd for `rv`:
======================= ===========================================
:attr:`response_class` the object is returned unchanged
:class:`str` a response object is created with the
string as body
:class:`unicode` a response object is created with the
string encoded to utf-8 as body
:class:`tuple` the response object is created with the
contents of the tuple as arguments
a WSGI function the function is called as WSGI application
and buffered as response object
======================= ===========================================
:param rv: the return value from the view function
"""
if isinstance(rv, self.response_class):
return rv
if isinstance(rv, basestring):
return self.response_class(rv)
if isinstance(rv, tuple):
return self.response_class(*rv)
return self.response_class.force_type(rv, request.environ)
def preprocess_request(self):
"""Called before the actual request dispatching and will
call every as :meth:`before_request` decorated function.
If any of these function returns a value it's handled as
if it was the return value from the view and further
request handling is stopped.
"""
for func in self.before_request_funcs:
rv = func()
if rv is not None:
return rv
def process_response(self, response):
"""Can be overridden in order to modify the response object
before it's sent to the WSGI server. By default this will
call all the :meth:`after_request` decorated functions.
:param response: a :attr:`response_class` object.
:return: a new response object or the same, has to be an
instance of :attr:`response_class`.
"""
session = _request_ctx_stack.top.session
if not isinstance(session, _NullSession):
self.save_session(session, response)
for handler in self.after_request_funcs:
response = handler(response)
return response
def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a
reference to the class. So instead of doing this::
app = MyMiddleware(app)
It's a better idea to do this instead::
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
:param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
"""
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
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)
:params environ: a WSGI environment
"""
return _RequestContext(self, environ)
def test_request_context(self, *args, **kwargs):
"""Creates a WSGI environment from the given values (see
:func:`werkzeug.create_environ` for more information, this
function accepts the same arguments).
"""
return self.request_context(create_environ(*args, **kwargs))
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)
# context locals
_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)

View file

@ -1,71 +0,0 @@
"""
Flask
-----
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good
intentions. And before you ask: It's BSD licensed!
Flask is Fun
````````````
::
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
And Easy to Setup
`````````````````
::
$ easy_install Flask
$ python hello.py
* Running on http://localhost:5000/
Links
`````
* `website <http://flask.pocoo.org/>`_
* `documentation <http://flask.pocoo.org/docs/>`_
* `development version
<http://github.com/mitsuhiko/flask/zipball/master#egg=Flask-dev>`_
"""
from setuptools import setup
setup(
name='Flask',
version='0.2',
url='http://github.com/mitsuhiko/flask/',
license='BSD',
author='Armin Ronacher',
author_email='armin.ronacher@active-4.com',
description='A microframework based on Werkzeug, Jinja2 '
'and good intentions',
long_description=__doc__,
py_modules=['flask'],
zip_safe=False,
platforms='any',
install_requires=[
'Werkzeug>=0.6.1',
'Jinja2>=2.4'
],
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
]
)

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

View file

@ -1,316 +0,0 @@
# -*- coding: utf-8 -*-
"""
Flask Tests
~~~~~~~~~~~
Tests Flask itself. The majority of Flask is already tested
as part of Werkzeug.
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from __future__ import with_statement
import os
import sys
import flask
import unittest
import tempfile
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
sys.path.append(os.path.join(example_path, 'flaskr'))
sys.path.append(os.path.join(example_path, 'minitwit'))
class ContextTestCase(unittest.TestCase):
def test_context_binding(self):
app = flask.Flask(__name__)
@app.route('/')
def index():
return 'Hello %s!' % flask.request.args['name']
@app.route('/meh')
def meh():
return flask.request.url
with app.test_request_context('/?name=World'):
assert index() == 'Hello World!'
with app.test_request_context('/meh'):
assert meh() == 'http://localhost/meh'
class BasicFunctionalityTestCase(unittest.TestCase):
def test_request_dispatching(self):
app = flask.Flask(__name__)
@app.route('/')
def index():
return flask.request.method
@app.route('/more', methods=['GET', 'POST'])
def more():
return flask.request.method
c = app.test_client()
assert c.get('/').data == 'GET'
rv = c.post('/')
assert rv.status_code == 405
assert sorted(rv.allow) == ['GET', 'HEAD']
rv = c.head('/')
assert rv.status_code == 200
assert not rv.data # head truncates
assert c.post('/more').data == 'POST'
assert c.get('/more').data == 'GET'
rv = c.delete('/more')
assert rv.status_code == 405
assert sorted(rv.allow) == ['GET', 'HEAD', 'POST']
def test_url_mapping(self):
app = flask.Flask(__name__)
def index():
return flask.request.method
def more():
return flask.request.method
app.add_url_rule('/', 'index', index)
app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'])
c = app.test_client()
assert c.get('/').data == 'GET'
rv = c.post('/')
assert rv.status_code == 405
assert sorted(rv.allow) == ['GET', 'HEAD']
rv = c.head('/')
assert rv.status_code == 200
assert not rv.data # head truncates
assert c.post('/more').data == 'POST'
assert c.get('/more').data == 'GET'
rv = c.delete('/more')
assert rv.status_code == 405
assert sorted(rv.allow) == ['GET', 'HEAD', 'POST']
def test_session(self):
app = flask.Flask(__name__)
app.secret_key = 'testkey'
@app.route('/set', methods=['POST'])
def set():
flask.session['value'] = flask.request.form['value']
return 'value set'
@app.route('/get')
def get():
return flask.session['value']
c = app.test_client()
assert c.post('/set', data={'value': '42'}).data == 'value set'
assert c.get('/get').data == '42'
def test_missing_session(self):
app = flask.Flask(__name__)
def expect_exception(f, *args, **kwargs):
try:
f(*args, **kwargs)
except RuntimeError, e:
assert e.args and 'session is unavailable' in e.args[0]
else:
assert False, 'expected exception'
with app.test_request_context():
assert flask.session.get('missing_key') is None
expect_exception(flask.session.__setitem__, 'foo', 42)
expect_exception(flask.session.pop, 'foo')
def test_flashes(self):
app = flask.Flask(__name__)
app.secret_key = 'testkey'
with app.test_request_context():
assert not flask.session.modified
flask.flash('Zap')
flask.session.modified = False
flask.flash('Zip')
assert flask.session.modified
assert list(flask.get_flashed_messages()) == ['Zap', 'Zip']
def test_request_processing(self):
app = flask.Flask(__name__)
evts = []
@app.before_request
def before_request():
evts.append('before')
@app.after_request
def after_request(response):
response.data += '|after'
evts.append('after')
return response
@app.route('/')
def index():
assert 'before' in evts
assert 'after' not in evts
return 'request'
assert 'after' not in evts
rv = app.test_client().get('/').data
assert 'after' in evts
assert rv == 'request|after'
def test_error_handling(self):
app = flask.Flask(__name__)
@app.errorhandler(404)
def not_found(e):
return 'not found', 404
@app.errorhandler(500)
def internal_server_error(e):
return 'internal server error', 500
@app.route('/')
def index():
flask.abort(404)
@app.route('/error')
def error():
1/0
c = app.test_client()
rv = c.get('/')
assert rv.status_code == 404
assert rv.data == 'not found'
rv = c.get('/error')
assert rv.status_code == 500
assert 'internal server error' in rv.data
def test_response_creation(self):
app = flask.Flask(__name__)
@app.route('/unicode')
def from_unicode():
return u'Hällo Wörld'
@app.route('/string')
def from_string():
return u'Hällo Wörld'.encode('utf-8')
@app.route('/args')
def from_tuple():
return 'Meh', 400, {'X-Foo': 'Testing'}, 'text/plain'
c = app.test_client()
assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8')
assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8')
rv = c.get('/args')
assert rv.data == 'Meh'
assert rv.headers['X-Foo'] == 'Testing'
assert rv.status_code == 400
assert rv.mimetype == 'text/plain'
def test_url_generation(self):
app = flask.Flask(__name__)
@app.route('/hello/<name>', methods=['POST'])
def hello():
pass
with app.test_request_context():
assert flask.url_for('hello', name='test x') == '/hello/test%20x'
def test_custom_converters(self):
from werkzeug.routing import BaseConverter
class ListConverter(BaseConverter):
def to_python(self, value):
return value.split(',')
def to_url(self, value):
return ','.join(super(ListConverter, self).to_url(x) for x in value)
app = flask.Flask(__name__)
app.url_map.converters['list'] = ListConverter
@app.route('/<list:args>')
def index(args):
return '|'.join(args)
c = app.test_client()
assert c.get('/1,2,3').data == '1|2|3'
def test_static_files(self):
app = flask.Flask(__name__)
rv = app.test_client().get('/static/index.html')
assert rv.status_code == 200
assert rv.data.strip() == '<h1>Hello World!</h1>'
with app.test_request_context():
assert flask.url_for('static', filename='index.html') \
== '/static/index.html'
class JSONTestCase(unittest.TestCase):
def test_jsonify(self):
d = dict(a=23, b=42, c=[1, 2, 3])
app = flask.Flask(__name__)
@app.route('/kw')
def return_kwargs():
return flask.jsonify(**d)
@app.route('/dict')
def return_dict():
return flask.jsonify(d)
c = app.test_client()
for url in '/kw', '/dict':
rv = c.get(url)
assert rv.mimetype == 'application/json'
assert flask.json.loads(rv.data) == d
def test_json_attr(self):
app = flask.Flask(__name__)
@app.route('/add', methods=['POST'])
def add():
return unicode(flask.request.json['a'] + flask.request.json['b'])
c = app.test_client()
rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
content_type='application/json')
assert rv.data == '3'
def test_template_escaping(self):
app = flask.Flask(__name__)
with app.test_request_context():
rv = flask.render_template_string('{{ "</script>"|tojson|safe }}')
assert rv == '"<\\/script>"'
rv = flask.render_template_string('{{ "<\0/script>"|tojson|safe }}')
assert rv == '"<\\u0000\\/script>"'
class TemplatingTestCase(unittest.TestCase):
def test_context_processing(self):
app = flask.Flask(__name__)
@app.context_processor
def context_processor():
return {'injected_value': 42}
@app.route('/')
def index():
return flask.render_template('context_template.html', value=23)
rv = app.test_client().get('/')
assert rv.data == '<p>23|42'
def test_escaping(self):
text = '<p>Hello World!'
app = flask.Flask(__name__)
@app.route('/')
def index():
return flask.render_template('escaping_template.html', text=text,
html=flask.Markup(text))
lines = app.test_client().get('/').data.splitlines()
assert lines == [
'&lt;p&gt;Hello World!',
'<p>Hello World!',
'<p>Hello World!',
'<p>Hello World!',
'&lt;p&gt;Hello World!',
'<p>Hello World!'
]
def test_macros(self):
app = flask.Flask(__name__)
with app.test_request_context():
macro = flask.get_template_attribute('_macro.html', 'hello')
assert macro('World') == 'Hello World!'
def suite():
from minitwit_tests import MiniTwitTestCase
from flaskr_tests import FlaskrTestCase
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ContextTestCase))
suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
suite.addTest(unittest.makeSuite(TemplatingTestCase))
if flask.json_available:
suite.addTest(unittest.makeSuite(JSONTestCase))
suite.addTest(unittest.makeSuite(MiniTwitTestCase))
suite.addTest(unittest.makeSuite(FlaskrTestCase))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='suite')

View file

@ -1 +0,0 @@
<h1>Hello World!</h1>

View file

@ -1 +0,0 @@
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}

View file

@ -1 +0,0 @@
<p>{{ value }}|{{ injected_value }}

View file

@ -1,6 +0,0 @@
{{ text }}
{{ html }}
{% autoescape false %}{{ text }}
{{ html }}{% endautoescape %}
{% autoescape true %}{{ text }}
{{ html }}{% endautoescape %}