.. _tutorial-views:

Step 6: The View Functions
==========================

Now that the database connections are working, you can start writing the
view functions.  You 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) will be on top.  The rows
returned from the cursor look a bit like dictionaries because we are using
the :class:`sqlite3.Row` row factory.

The view function will pass the entries to the :file:`show_entries.html`
template and return the rendered one::

    @app.route('/')
    def show_entries():
        db = get_db()
        cur = db.execute('select title, text from entries order by id desc')
        entries = cur.fetchall()
        return render_template('show_entries.html', entries=entries)

Add New Entry
-------------

This view lets the user add new entries if they are logged in.  This only
responds to ``POST`` requests; the actual form is shown on the
`show_entries` page.  If everything worked out well, it 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)
        db = get_db()
        db.execute('insert into entries (title, text) values (?, ?)',
                     [request.form['title'], request.form['text']])
        db.commit()
        flash('New entry was successfully posted')
        return redirect(url_for('show_entries'))

Note that this view checks that the user is logged in (that is, if the
`logged_in` key is present in the session and ``True``).

.. admonition:: Security Note

   Be sure to use question marks when building SQL statements, as done in the
   example above.  Otherwise, your app will be vulnerable to SQL injection when
   you use string formatting to build SQL statements.
   See :ref:`sqlite3` for more.

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 for 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 addition, a message is flashed that informs the user that he or
she was logged in successfully.  If an error occurred, the template is
notified about that, and the user is asked again::

    @app.route('/login', methods=['GET', 'POST'])
    def login():
        error = None
        if request.method == 'POST':
            if request.form['username'] != app.config['USERNAME']:
                error = 'Invalid username'
            elif request.form['password'] != app.config['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.  There is 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 is not in there.  This is helpful because now it is not necessary to
check 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'))

.. admonition:: Security Note

    Passwords should never be stored in plain text in a production
    system. This tutorial uses plain text passwords for simplicity. If you
    plan to release a project based off this tutorial out into the world,
    passwords should be both `hashed and salted`_ before being stored in a
    database or file.

    Fortunately, there are Flask extensions for the purpose of
    hashing passwords and verifying passwords against hashes, so adding
    this functionality is fairly straight forward. There are also
    many general python libraries that can be used for hashing.

    You can find a list of recommended Flask extensions
    `here <http://flask.pocoo.org/extensions/>`_


Continue with :ref:`tutorial-templates`.

.. _hashed and salted: https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
