Browse Source

Address #1902: Converts example/flaskr to have a setup.py (#1945)

* Converts example/flaskr to have a setup.py

Makes the flaskr app easier to run, ex. workflow:
- pip install --editable .
- export FLASK_APP=flaskr.flaskr
- flask initdb
- flask run

Testing is also easier now:
- python setup.py test

* Fixed an import error in flaskr/tests

- the statement `import flaskr` caused errors in python3
- `from . import flaskr` fixes the issue in 2.7.11 and 3.5.1

* Better project structure and updates the docs

- Re-factors *flaskr*'s project structure a bit
- Updates docs to make sense with the new structure
  - Adds a new step about installing Flask apps with setuptools
  - Switches first-person style writing to second-person (reads better IMO)
  - Adds segments in *testing.rst* for running tests with setuptools

* Remove __init__.py from tests

- py.test recommends not using __init__.py

* Fix testing import errors
pull/1953/head
Kyle Lawlor 9 years ago committed by David Lord
parent
commit
17d4cb3828
  1. 4
      docs/tutorial/css.rst
  2. 28
      docs/tutorial/dbcon.rst
  3. 29
      docs/tutorial/dbinit.rst
  4. 26
      docs/tutorial/folders.rst
  5. 1
      docs/tutorial/index.rst
  6. 9
      docs/tutorial/introduction.rst
  7. 10
      docs/tutorial/schema.rst
  8. 73
      docs/tutorial/setup.rst
  9. 89
      docs/tutorial/setuptools.rst
  10. 15
      docs/tutorial/templates.rst
  11. 78
      docs/tutorial/testing.rst
  12. 43
      docs/tutorial/views.rst
  13. 1
      examples/flaskr/.gitignore
  14. 3
      examples/flaskr/MANIFEST.in
  15. 14
      examples/flaskr/README
  16. 0
      examples/flaskr/flaskr/__init__.py
  17. 0
      examples/flaskr/flaskr/flaskr.py
  18. 0
      examples/flaskr/flaskr/schema.sql
  19. 0
      examples/flaskr/flaskr/static/style.css
  20. 0
      examples/flaskr/flaskr/templates/layout.html
  21. 0
      examples/flaskr/flaskr/templates/login.html
  22. 0
      examples/flaskr/flaskr/templates/show_entries.html
  23. 2
      examples/flaskr/setup.cfg
  24. 16
      examples/flaskr/setup.py
  25. 6
      examples/flaskr/tests/context.py
  26. 3
      examples/flaskr/tests/test_flaskr.py

4
docs/tutorial/css.rst

@ -1,11 +1,11 @@
.. _tutorial-css: .. _tutorial-css:
Step 7: Adding Style Step 9: Adding Style
==================== ====================
Now that everything else works, it's time to add some style to the Now that everything else works, it's time to add some style to the
application. Just create a stylesheet called :file:`style.css` in the application. Just create a stylesheet called :file:`style.css` in the
:file:`static` folder we created before: :file:`static` folder:
.. sourcecode:: css .. sourcecode:: css

28
docs/tutorial/dbcon.rst

@ -1,25 +1,23 @@
.. _tutorial-dbcon: .. _tutorial-dbcon:
Step 3: Database Connections Step 4: Database Connections
---------------------------- ----------------------------
We have created a function for establishing a database connection with You now have a function for establishing a database connection with
`connect_db`, but by itself, that's not particularly useful. Creating and `connect_db`, but by itself, it is not particularly useful. Creating and
closing database connections all the time is very inefficient, so we want closing database connections all the time is very inefficient, so you will
to keep it around for longer. Because database connections encapsulate a need to keep it around for longer. Because database connections
transaction, we also need to make sure that only one request at the time encapsulate a transaction, you will need to make sure that only one
uses the connection. How can we elegantly do that with Flask? request at a time uses the connection. An elegant way to do this is by
utilizing the *application context*.
This is where the application context comes into play, so let's start Flask provides two contexts: the *application context* and the
there. *request context*. For the time being, all you have to know is that there
Flask provides us with two contexts: the application context and the
request context. For the time being, all you have to know is that there
are special variables that use these. For instance, the are special variables that use these. For instance, the
:data:`~flask.request` variable is the request object associated with :data:`~flask.request` variable is the request object associated with
the current request, whereas :data:`~flask.g` is a general purpose the current request, whereas :data:`~flask.g` is a general purpose
variable associated with the current application context. We will go into variable associated with the current application context. The tutorial
the details of this a bit later. will cover some more details of this later on.
For the time being, all you have to know is that you can store information For the time being, all you have to know is that you can store information
safely on the :data:`~flask.g` object. safely on the :data:`~flask.g` object.
@ -37,7 +35,7 @@ already established connection::
g.sqlite_db = connect_db() g.sqlite_db = connect_db()
return g.sqlite_db return g.sqlite_db
So now we know how to connect, but how do we properly disconnect? For Now you know how to connect, but how can you properly disconnect? For
that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext` that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext`
decorator. It's executed every time the application context tears down:: decorator. It's executed every time the application context tears down::

29
docs/tutorial/dbinit.rst

@ -1,6 +1,6 @@
.. _tutorial-dbinit: .. _tutorial-dbinit:
Step 4: Creating The Database Step 5: Creating The Database
============================= =============================
As outlined earlier, Flaskr is a database powered application, and more As outlined earlier, Flaskr is a database powered application, and more
@ -16,13 +16,14 @@ Such a schema can be created by piping the ``schema.sql`` file into the
The downside of this is that it requires the ``sqlite3`` command to be The downside of this is that it requires the ``sqlite3`` command to be
installed, which is not necessarily the case on every system. This also installed, which is not necessarily the case on every system. This also
requires that we provide the path to the database, which can introduce requires that you provide the path to the database, which can introduce
errors. It's a good idea to add a function that initializes the database errors. It's a good idea to add a function that initializes the database
for you to the application. for you, to the application.
To do this, we can create a function and hook it into the :command:`flask` To do this, you can create a function and hook it into a :command:`flask`
command that initializes the database. Let me show you the code first. Just command that initializes the database. For now just take a look at the
add this function below the `connect_db` function in :file:`flaskr.py`:: code segment below. A good place to add this function, and command, is
just below the `connect_db` function in :file:`flaskr.py`::
def init_db(): def init_db():
db = get_db() db = get_db()
@ -38,23 +39,23 @@ add this function below the `connect_db` function in :file:`flaskr.py`::
The ``app.cli.command()`` decorator registers a new command with the The ``app.cli.command()`` decorator registers a new command with the
:command:`flask` script. When the command executes, Flask will automatically :command:`flask` script. When the command executes, Flask will automatically
create an application context for us bound to the right application. create an application context which is bound to the right application.
Within the function, we can then access :attr:`flask.g` and other things as Within the function, you can then access :attr:`flask.g` and other things as
we would expect. When the script ends, the application context tears down you might expect. When the script ends, the application context tears down
and the database connection is released. and the database connection is released.
We want to keep an actual function around that initializes the database, You will want to keep an actual function around that initializes the database,
though, so that we can easily create databases in unit tests later on. (For though, so that we can easily create databases in unit tests later on. (For
more information see :ref:`testing`.) more information see :ref:`testing`.)
The :func:`~flask.Flask.open_resource` method of the application object The :func:`~flask.Flask.open_resource` method of the application object
is a convenient helper function that will open a resource that the is a convenient helper function that will open a resource that the
application provides. This function opens a file from the resource application provides. This function opens a file from the resource
location (your ``flaskr`` folder) and allows you to read from it. We are location (the :file:`flaskr/flaskr` folder) and allows you to read from it.
using this here to execute a script on the database connection. It is used in this example to execute a script on the database connection.
The connection object provided by SQLite can give us a cursor object. The connection object provided by SQLite can give you a cursor object.
On that cursor, there is a method to execute a complete script. Finally, we On that cursor, there is a method to execute a complete script. Finally, you
only have to commit the changes. SQLite3 and other transactional only have to commit the changes. SQLite3 and other transactional
databases will not commit unless you explicitly tell it to. databases will not commit unless you explicitly tell it to.

26
docs/tutorial/folders.rst

@ -3,21 +3,25 @@
Step 0: Creating The Folders Step 0: Creating The Folders
============================ ============================
Before we get started, let's create the folders needed for this Before getting started, you will need to create the folders needed for this
application:: application::
/flaskr /flaskr
/static /flaskr
/templates /static
/templates
The ``flaskr`` folder is not a Python package, but just something where we The application will be installed and run as Python package. This is the
drop our files. Later on, we will put our database schema as well as main recommended way to install and run Flask applications. You will see exactly
module into this folder. It is done in the following way. The files inside how to run ``flaskr`` later on in this tutorial. For now go ahead and create
the :file:`static` folder are available to users of the application via HTTP. the applications directory structure. In the next few steps you will be
This is the place where CSS and Javascript files go. Inside the creating the database schema as well as the main module.
:file:`templates` folder, Flask will look for `Jinja2`_ templates. The
templates you create later on in the tutorial will go in this directory.
Continue with :ref:`tutorial-schema`. As a quick side note, the files inside of the :file:`static` folder are
available to users of the application via HTTP. This is the place where CSS and
Javascript files go. Inside the :file:`templates` folder, Flask will look for
`Jinja2`_ templates. You will see examples of this later on.
For now you should continue with :ref:`tutorial-schema`.
.. _Jinja2: http://jinja.pocoo.org/ .. _Jinja2: http://jinja.pocoo.org/

1
docs/tutorial/index.rst

@ -24,6 +24,7 @@ the `example source`_.
folders folders
schema schema
setup setup
setuptools
dbcon dbcon
dbinit dbinit
views views

9
docs/tutorial/introduction.rst

@ -3,8 +3,9 @@
Introducing Flaskr Introducing Flaskr
================== ==================
We will call our blogging application Flaskr, but feel free to choose your own This tutorial will demonstrate a blogging application named Flaskr, but feel
less Web-2.0-ish name ;) Essentially, we want it to do the following things: free to choose your own less Web-2.0-ish name ;) Essentially, it will do the
following things:
1. Let the user sign in and out with credentials specified in the 1. Let the user sign in and out with credentials specified in the
configuration. Only one user is supported. configuration. Only one user is supported.
@ -14,8 +15,8 @@ less Web-2.0-ish name ;) Essentially, we want it to do the following things:
3. The index page shows all entries so far in reverse chronological order 3. The index page shows all entries so far in reverse chronological order
(newest on top) and the user can add new ones from there if logged in. (newest on top) and the user can add new ones from there if logged in.
We will be using SQLite3 directly for this application because it's good SQLite3 will be used directly for this application because it's good enough
enough for an application of this size. For larger applications, however, for an application of this size. For larger applications, however,
it makes a lot of sense to use `SQLAlchemy`_, as it handles database it makes a lot of sense to use `SQLAlchemy`_, as it handles database
connections in a more intelligent way, allowing you to target different connections in a more intelligent way, allowing you to target different
relational databases at once and more. You might also want to consider relational databases at once and more. You might also want to consider

10
docs/tutorial/schema.rst

@ -3,10 +3,10 @@
Step 1: Database Schema Step 1: Database Schema
======================= =======================
First, we want to create the database schema. Only a single table is needed In this step, you will create the database schema. Only a single table is
for this application and we only want to support SQLite, so creating the needed for this application and it will only support SQLite. All you need to do
database schema is quite easy. Just put the following contents into a file is put the following contents into a file named :file:`schema.sql` in the
named `schema.sql` in the just created `flaskr` folder: :file:`flaskr/flaskr` folder:
.. sourcecode:: sql .. sourcecode:: sql
@ -17,7 +17,7 @@ named `schema.sql` in the just created `flaskr` folder:
'text' text not null 'text' text not null
); );
This schema consists of a single table called ``entries``. Each row in This schema consists of a single table called ``entries``. Each row in
this table has an ``id``, a ``title``, and a ``text``. The ``id`` is an 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 automatically incrementing integer and a primary key, the other two are
strings that must not be null. strings that must not be null.

73
docs/tutorial/setup.rst

@ -3,15 +3,16 @@
Step 2: Application Setup Code Step 2: Application Setup Code
============================== ==============================
Now that we have the schema in place, we can create the application module. Now that the schema is in place, you can create the application module,
Let's call it ``flaskr.py``. We will place this file inside the ``flaskr`` :file:`flaskr.py`. This file should be placed inside of the
folder. We will begin by adding the imports we need and by adding the config :file:`flaskr/flaskr` folder. The first several lines of code in the
section. For small applications, it is possible to drop the configuration application module are the needed import statements. After that there will be a
directly into the module, and this is what we will be doing here. However, few lines of configuration code. For small applications like ``flaskr``, it is
a cleaner solution would be to create a separate ``.ini`` or ``.py`` file, possible to drop the configuration directly into the module. However, a cleaner
load that, and import the values from there. solution is to create a separate ``.ini`` or ``.py`` file, load that, and
import the values from there.
First, we add the imports in :file:`flaskr.py`:: Here are the import statements (in :file:`flaskr.py`)::
# all the imports # all the imports
import os import os
@ -19,12 +20,13 @@ First, we add the imports in :file:`flaskr.py`::
from flask import Flask, request, session, g, redirect, url_for, abort, \ from flask import Flask, request, session, g, redirect, url_for, abort, \
render_template, flash render_template, flash
Next, we can create our actual application and initialize it with the The next couple lines will create the actual application instance and
config from the same file in :file:`flaskr.py`:: initialize it with the config from the same file in :file:`flaskr.py`:
# create our little application :) .. sourcecode:: python
app = Flask(__name__)
app.config.from_object(__name__) app = Flask(__name__) # create the application instance :)
app.config.from_object(__name__) # load config from this file , flaskr.py
# Load default config and override config from an environment variable # Load default config and override config from an environment variable
app.config.update(dict( app.config.update(dict(
@ -35,8 +37,8 @@ config from the same file in :file:`flaskr.py`::
)) ))
app.config.from_envvar('FLASKR_SETTINGS', silent=True) app.config.from_envvar('FLASKR_SETTINGS', silent=True)
The :class:`~flask.Config` object works similarly to a dictionary so we The :class:`~flask.Config` object works similarly to a dictionary, so it can be
can update it with new values. updated with new values.
.. admonition:: Database Path .. admonition:: Database Path
@ -55,10 +57,10 @@ can update it with new values.
Usually, it is a good idea to load a separate, environment-specific Usually, it is a good idea to load a separate, environment-specific
configuration file. Flask allows you to import multiple configurations and it configuration file. Flask allows you to import multiple configurations and it
will use the setting defined in the last import. This enables robust will use the setting defined in the last import. This enables robust
configuration setups. :meth:`~flask.Config.from_envvar` can help achieve this. configuration setups. :meth:`~flask.Config.from_envvar` can help achieve this.
.. code-block:: python .. sourcecode:: python
app.config.from_envvar('FLASKR_SETTINGS', silent=True) app.config.from_envvar('FLASKR_SETTINGS', silent=True)
@ -74,15 +76,15 @@ that in all cases, only variable names that are uppercase are considered.
The ``SECRET_KEY`` is needed to keep the client-side sessions secure. 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. Choose that key wisely and as hard to guess and complex as possible.
We will also add a method that allows for easy connections to the Lastly, you will add a method that allows for easy connections to the
specified database. This can be used to open a connection on request and specified database. This can be used to open a connection on request and
also from the interactive Python shell or a script. This will come in also from the interactive Python shell or a script. This will come in
handy later. We create a simple database connection through SQLite and handy later. You can create a simple database connection through SQLite and
then tell it to use the :class:`sqlite3.Row` object to represent rows. then tell it to use the :class:`sqlite3.Row` object to represent rows.
This allows us to treat the rows as if they were dictionaries instead of This allows the rows to be treated as if they were dictionaries instead of
tuples. tuples.
:: .. sourcecode:: python
def connect_db(): def connect_db():
"""Connects to the specific database.""" """Connects to the specific database."""
@ -90,29 +92,6 @@ tuples.
rv.row_factory = sqlite3.Row rv.row_factory = sqlite3.Row
return rv return rv
With that out of the way, you should be able to start up the application In the next section you will see how to run the application.
without problems. Do this with the following commands::
export FLASK_APP=flaskr
export FLASK_DEBUG=1
flask run
(In case you are on Windows you need to use `set` instead of `export`).
The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger.
*Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server!
You will see a message telling you that server has started along with
the address at which you can access it.
When you head over to the server in your browser, you will get a 404 error
because we don't have any views yet. We will focus on that a little later,
but first, we should get the database working.
.. admonition:: Externally Visible Server
Want your server to be publicly available? Check out the
:ref:`externally visible server <public-server>` section for more
information.
Continue with :ref:`tutorial-dbcon`. Continue with :ref:`tutorial-setuptools`.

89
docs/tutorial/setuptools.rst

@ -0,0 +1,89 @@
.. _tutorial-setuptools:
Step 3: Installing flaskr with setuptools
=========================================
Flask is now shipped with built-in support for `Click`_. Click provides
Flask with enhanced and extensible command line utilities. Later in this
tutorial you will see exactly how to extend the ``flask`` command line
interface (CLI).
A useful pattern to manage a Flask application is to install your app
using `setuptools`_. This involves creating a :file:`setup.py`
in the projects root directory. You also need to add an empty
:file:`__init__.py` file to make the :file:`flaskr/flaskr` directory
a package. The code structure at this point should be::
/flaskr
/flaskr
__init__.py
/static
/templates
setup.py
The content of the ``setup.py`` file for ``flaskr`` is:
.. sourcecode:: python
from setuptools import setup
setup(
name='flaskr',
packages=['flaskr'],
include_package_data=True,
install_requires=[
'flask',
],
)
When using setuptools, it is also necessary to specify any special files
that should be included in your package (in the :file:`MANIFEST.in`).
In this case, the static and templates directories need to be included,
as well as the schema. Create the :file:`MANIFEST.in` and add the
following lines::
graft flaskr/templates
graft flaskr/static
include flaskr/schema.sql
At this point you should be able to install the application. As usual, it
is recommended to install your Flask application within a `virtualenv`_.
With that said, go ahead and install the application with::
pip install --editable .
.. note:: The above installation command assumes that it is run within the
projects root directory, `flaskr/`. Also, the `editable` flag allows
editing source code without having to reinstall the Flask app each time
you make changes.
With that out of the way, you should be able to start up the application.
Do this with the following commands::
export FLASK_APP=flaskr.flaskr
export FLASK_DEBUG=1
flask run
(In case you are on Windows you need to use `set` instead of `export`).
The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger.
*Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server!
You will see a message telling you that server has started along with
the address at which you can access it.
When you head over to the server in your browser, you will get a 404 error
because we don't have any views yet. That will be addressed a little later,
but first, you should get the database working.
.. admonition:: Externally Visible Server
Want your server to be publicly available? Check out the
:ref:`externally visible server <public-server>` section for more
information.
Continue with :ref:`tutorial-dbcon`.
.. _Click: http://click.pocoo.org
.. _setuptools: https://setuptools.readthedocs.io
.. _virtualenv: https://virtualenv.pypa.io

15
docs/tutorial/templates.rst

@ -1,11 +1,12 @@
.. _tutorial-templates: .. _tutorial-templates:
Step 6: The Templates Step 8: The Templates
===================== =====================
Now we should start working on the templates. If we were to request the URLs Now it is time to start working on the templates. As you may have
now, we would only get an exception that Flask cannot find the templates. The noticed, if you make requests with the app running, you will get
templates are using `Jinja2`_ syntax and have autoescaping enabled by 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 default. This means that unless you mark a value in the code with
:class:`~flask.Markup` or with the ``|safe`` filter in the template, :class:`~flask.Markup` or with the ``|safe`` filter in the template,
Jinja2 will ensure that special characters such as ``<`` or ``>`` are Jinja2 will ensure that special characters such as ``<`` or ``>`` are
@ -57,9 +58,9 @@ show_entries.html
This template extends the :file:`layout.html` template from above to display the This template extends the :file:`layout.html` template from above to display the
messages. Note that the ``for`` loop iterates over the messages we passed messages. Note that the ``for`` loop iterates over the messages we passed
in with the :func:`~flask.render_template` function. We also tell the in with the :func:`~flask.render_template` function. Notice that the form is
form to submit to your `add_entry` function and use ``POST`` as HTTP configured to to submit to the `add_entry` view function and use ``POST`` as
method: HTTP method:
.. sourcecode:: html+jinja .. sourcecode:: html+jinja

78
docs/tutorial/testing.rst

@ -8,3 +8,81 @@ expected, it's probably not a bad idea to add automated tests to simplify
modifications in the future. The application above is used as a basic modifications in the future. The application above is used as a basic
example of how to perform unit testing in the :ref:`testing` section of the example of how to perform unit testing in the :ref:`testing` section of the
documentation. Go there to see how easy it is to test Flask applications. documentation. Go there to see how easy it is to test Flask applications.
Adding Tests to flaskr
======================
Assuming you have seen the testing section above and have either written
your own tests for ``flaskr`` or have followed along with the examples
provided, you might be wondering about ways to organize the project.
One possible and recommended project structure is::
flaskr/
flaskr/
__init__.py
static/
templates/
tests/
context.py
test_flaskr.py
setup.py
MANIFEST.in
For now go ahead a create the :file:`tests/` directory as well as the
:file:`context.py` and :file:`test_flaskr.py` files, if you haven't
already. The context file is used as an import helper. The contents
of that file are::
import sys, os
basedir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, basedir + '/../')
from flaskr import flaskr
Testing + Setuptools
====================
One way to handle testing is to integrate it with ``setuptools``. All it
requires is adding a couple of lines to the :file:`setup.py` file and
creating a new file :file:`setup.cfg`. Go ahead and update the
:file:`setup.py` to contain::
from setuptools import setup
setup(
name='flaskr',
packages=['flaskr'],
include_package_data=True,
install_requires=[
'flask',
],
)
setup_requires=[
'pytest-runner',
],
tests_require=[
'pytest',
],
)
Now create :file:`setup.cfg` in the project root (alongside
:file:`setup.py`)::
[aliases]
test=pytest
Now you can run::
python setup.py test
This calls on the alias created in :file:`setup.cfg` which in turn runs
``pytest`` via ``pytest-runner``, as the :file:`setup.py` script has
been called. (Recall the `setup_requires` argument in :file:`setup.py`)
Following the standard rules of test-discovery your tests will be
found, run, and hopefully pass.
This is one possible way to run and manage testing. Here ``pytest`` is
used, but there are other options such as ``nose``. Integrating testing
with ``setuptools`` is convenient because it is not necessary to actually
download ``pytest`` or any other testing framework one might use.

43
docs/tutorial/views.rst

@ -1,10 +1,10 @@
.. _tutorial-views: .. _tutorial-views:
Step 5: The View Functions Step 7: The View Functions
========================== ==========================
Now that the database connections are working, we can start writing the Now that the database connections are working, you can start writing the
view functions. We will need four of them: view functions. You will need four of them:
Show Entries Show Entries
------------ ------------
@ -30,7 +30,7 @@ Add New Entry
This view lets the user add new entries if they are logged in. This only 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 responds to ``POST`` requests; the actual form is shown on the
`show_entries` page. If everything worked out well, we will `show_entries` page. If everything worked out well, it will
:func:`~flask.flash` an information message to the next request and :func:`~flask.flash` an information message to the next request and
redirect back to the `show_entries` page:: redirect back to the `show_entries` page::
@ -45,8 +45,8 @@ redirect back to the `show_entries` page::
flash('New entry was successfully posted') flash('New entry was successfully posted')
return redirect(url_for('show_entries')) return redirect(url_for('show_entries'))
Note that we check that the user is logged in here (the `logged_in` key is Note that this view checks that the user is logged in (that is, if the
present in the session and ``True``). `logged_in` key is present in the session and ``True``).
.. admonition:: Security Note .. admonition:: Security Note
@ -81,11 +81,11 @@ notified about that, and the user is asked again::
return render_template('login.html', error=error) return render_template('login.html', error=error)
The `logout` function, on the other hand, removes that key from the session 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 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 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 will delete the key from the dictionary if present or do nothing when that
key is not in there. This is helpful because now we don't have to check key is not in there. This is helpful because now it is not necessary to
if the user was logged in. check if the user was logged in.
:: ::
@ -94,11 +94,24 @@ if the user was logged in.
session.pop('logged_in', None) session.pop('logged_in', None)
flash('You were logged out') flash('You were logged out')
return redirect(url_for('show_entries')) return redirect(url_for('show_entries'))
Note that it is not a good idea to store passwords in plain text. You want to .. admonition:: Security Note
protect login credentials if someone happens to have access to your database.
One way to do this is to use Security Helpers from Werkzeug to hash the Passwords should never be stored in plain text in a production
password. However, the emphasis of this tutorial is to demonstrate the basics system. This tutorial uses plain text passwords for simplicity. If you
of Flask and plain text passwords are used for simplicity. 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`. Continue with :ref:`tutorial-templates`.
.. _hashed and salted: https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/

1
examples/flaskr/.gitignore vendored

@ -1 +1,2 @@
flaskr.db flaskr.db
.eggs/

3
examples/flaskr/MANIFEST.in

@ -0,0 +1,3 @@
graft flaskr/templates
graft flaskr/static
include flaskr/schema.sql

14
examples/flaskr/README

@ -13,15 +13,19 @@
export an FLASKR_SETTINGS environment variable export an FLASKR_SETTINGS environment variable
pointing to a configuration file. pointing to a configuration file.
2. Instruct flask to use the right application 2. install the app from the root of the project directory
export FLASK_APP=flaskr pip install --editable .
3. initialize the database with this command: 3. Instruct flask to use the right application
export FLASK_APP=flaskr.flaskr
4. initialize the database with this command:
flask initdb flask initdb
4. now you can run flaskr: 5. now you can run flaskr:
flask run flask run
@ -30,5 +34,5 @@
~ Is it tested? ~ Is it tested?
You betcha. Run the `test_flaskr.py` file to see You betcha. Run `python setup.py test` to see
the tests pass. the tests pass.

0
examples/flaskr/flaskr/__init__.py

0
examples/flaskr/flaskr.py → examples/flaskr/flaskr/flaskr.py

0
examples/flaskr/schema.sql → examples/flaskr/flaskr/schema.sql

0
examples/flaskr/static/style.css → examples/flaskr/flaskr/static/style.css

0
examples/flaskr/templates/layout.html → examples/flaskr/flaskr/templates/layout.html

0
examples/flaskr/templates/login.html → examples/flaskr/flaskr/templates/login.html

0
examples/flaskr/templates/show_entries.html → examples/flaskr/flaskr/templates/show_entries.html

2
examples/flaskr/setup.cfg

@ -0,0 +1,2 @@
[aliases]
test=pytest

16
examples/flaskr/setup.py

@ -0,0 +1,16 @@
from setuptools import setup
setup(
name='flaskr',
packages=['flaskr'],
include_package_data=True,
install_requires=[
'flask',
],
setup_requires=[
'pytest-runner',
],
tests_require=[
'pytest',
],
)

6
examples/flaskr/tests/context.py

@ -0,0 +1,6 @@
import sys, os
basedir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, basedir + '/../')
from flaskr import flaskr

3
examples/flaskr/test_flaskr.py → examples/flaskr/tests/test_flaskr.py

@ -10,11 +10,10 @@
""" """
import pytest import pytest
import os import os
import flaskr
import tempfile import tempfile
from context import flaskr
@pytest.fixture @pytest.fixture
def client(request): def client(request):
Loading…
Cancel
Save