diff --git a/docs/api.rst b/docs/api.rst
index 5ab86e7d..6c763393 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -68,6 +68,10 @@ Incoming Request Data
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.)
@@ -136,6 +140,22 @@ To access the current session you can use the :class:`session` object:
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
----------------------------
diff --git a/docs/index.rst b/docs/index.rst
index 217ced04..5e5d8622 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,4 +8,6 @@ Contents:
.. toctree::
:maxdepth: 2
+ installation
+ quickstart
api
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 00000000..8b0804d2
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,6 @@
+.. _installation:
+
+Installation
+============
+
+Blafasel, add me
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 00000000..0bfd7df7
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,312 @@
+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()
+
+If you now start that application with your Python interpreter and head
+over to `http://localhost:5000/ `_, you should see
+your hello world application.
+
+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.
+
+
+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
+````. Such a part is then passed as keyword argument to
+your function. Optionally a converter can be specifed by specifying a
+rule with ````. Here some nice examples::
+
+ @app.route('/user/')
+ def show_user_profile(username):
+ # show the user profile for that user
+ pass
+
+ @app.route('/post/')
+ 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
+=========== ===========================================
+
+
+HTTP Methods
+````````````
+
+HTTP 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 RFC demands, so you can completely ignore
+that part of the HTTP specification.
+
+
+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
+``````````````
+
+.. 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.
+
+.. _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 '''
+
+ '''
+
+ @app.route('/logout')
+ def logout():
+ # remove the username from the session if its there
+ session.pop('username', None)
diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py
index fccf969c..ebb304a7 100644
--- a/examples/minitwit/minitwit.py
+++ b/examples/minitwit/minitwit.py
@@ -148,7 +148,7 @@ def unfollow_user(username):
return redirect(url_for('user_timeline', username=username))
-@app.route('/add_message')
+@app.route('/add_message', methods=['POST'])
def add_message():
if 'user_id' not in session:
abort(401)
@@ -161,7 +161,7 @@ def add_message():
return redirect(url_for('timeline'))
-@app.route('/login')
+@app.route('/login', methods=['GET', 'POST'])
def login():
if 'user_id' in session:
return redirect(url_for('timeline'))
@@ -181,7 +181,7 @@ def login():
return render_template('login.html', error=error)
-@app.route('/register')
+@app.route('/register', methods=['GET', 'POST'])
def register():
if 'user_id' in session:
return redirect(url_for('timeline'))
diff --git a/flask.py b/flask.py
index 794f234a..9ea9db5c 100644
--- a/flask.py
+++ b/flask.py
@@ -326,11 +326,25 @@ class Flask(object):
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 methods: a list of methods this rule should be limited
- to (``GET``, ``POST`` etc.)
+ 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
@@ -339,6 +353,7 @@ class Flask(object):
def decorator(f):
if 'endpoint' not in options:
options['endpoint'] = f.__name__
+ options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))
self.view_functions[options['endpoint']] = f
return f