* 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
This commit is contained in:
parent
1ffd07ff5a
commit
17d4cb3828
26 changed files with 323 additions and 127 deletions
|
|
@ -1,11 +1,11 @@
|
|||
.. _tutorial-css:
|
||||
|
||||
Step 7: Adding Style
|
||||
Step 9: Adding Style
|
||||
====================
|
||||
|
||||
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
|
||||
:file:`static` folder we created before:
|
||||
:file:`static` folder:
|
||||
|
||||
.. sourcecode:: css
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
.. _tutorial-dbcon:
|
||||
|
||||
Step 3: Database Connections
|
||||
Step 4: Database Connections
|
||||
----------------------------
|
||||
|
||||
We have created a function for establishing a database connection with
|
||||
`connect_db`, but by itself, that's not particularly useful. Creating and
|
||||
closing database connections all the time is very inefficient, so we want
|
||||
to keep it around for longer. Because database connections encapsulate a
|
||||
transaction, we also need to make sure that only one request at the time
|
||||
uses the connection. How can we elegantly do that with Flask?
|
||||
You now have a function for establishing a database connection with
|
||||
`connect_db`, but by itself, it is not particularly useful. Creating and
|
||||
closing database connections all the time is very inefficient, so you will
|
||||
need to keep it around for longer. Because database connections
|
||||
encapsulate a transaction, you will need to make sure that only one
|
||||
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
|
||||
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
|
||||
Flask provides 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
|
||||
:data:`~flask.request` variable is the request object associated with
|
||||
the current request, whereas :data:`~flask.g` is a general purpose
|
||||
variable associated with the current application context. We will go into
|
||||
the details of this a bit later.
|
||||
variable associated with the current application context. The tutorial
|
||||
will cover some more details of this later on.
|
||||
|
||||
For the time being, all you have to know is that you can store information
|
||||
safely on the :data:`~flask.g` object.
|
||||
|
|
@ -37,7 +35,7 @@ already established connection::
|
|||
g.sqlite_db = connect_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`
|
||||
decorator. It's executed every time the application context tears down::
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.. _tutorial-dbinit:
|
||||
|
||||
Step 4: Creating The Database
|
||||
Step 5: Creating The Database
|
||||
=============================
|
||||
|
||||
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
|
||||
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
|
||||
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`
|
||||
command that initializes the database. Let me show you the code first. Just
|
||||
add this function below the `connect_db` function in :file:`flaskr.py`::
|
||||
To do this, you can create a function and hook it into a :command:`flask`
|
||||
command that initializes the database. For now just take a look at the
|
||||
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():
|
||||
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
|
||||
:command:`flask` script. When the command executes, Flask will automatically
|
||||
create an application context for us bound to the right application.
|
||||
Within the function, we can then access :attr:`flask.g` and other things as
|
||||
we would expect. When the script ends, the application context tears down
|
||||
create an application context which is bound to the right application.
|
||||
Within the function, you can then access :attr:`flask.g` and other things as
|
||||
you might expect. When the script ends, the application context tears down
|
||||
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
|
||||
more information see :ref:`testing`.)
|
||||
|
||||
The :func:`~flask.Flask.open_resource` method of the application object
|
||||
is a convenient helper function that will open a resource that the
|
||||
application provides. 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.
|
||||
location (the :file:`flaskr/flaskr` folder) and allows you to read from it.
|
||||
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.
|
||||
On that cursor, there is a method to execute a complete script. Finally, we
|
||||
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, you
|
||||
only have to commit the changes. SQLite3 and other transactional
|
||||
databases will not commit unless you explicitly tell it to.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,25 @@
|
|||
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::
|
||||
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
|
||||
The ``flaskr`` folder is not a Python package, but just something where we
|
||||
drop our files. Later on, we will put our database schema as well as main
|
||||
module into this folder. It is done in the following way. The files inside
|
||||
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. The
|
||||
templates you create later on in the tutorial will go in this directory.
|
||||
The application will be installed and run as Python package. This is the
|
||||
recommended way to install and run Flask applications. You will see exactly
|
||||
how to run ``flaskr`` later on in this tutorial. For now go ahead and create
|
||||
the applications directory structure. In the next few steps you will be
|
||||
creating the database schema as well as the main module.
|
||||
|
||||
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/
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ the `example source`_.
|
|||
folders
|
||||
schema
|
||||
setup
|
||||
setuptools
|
||||
dbcon
|
||||
dbinit
|
||||
views
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
Introducing Flaskr
|
||||
==================
|
||||
|
||||
We will call our blogging application Flaskr, but feel free to choose your own
|
||||
less Web-2.0-ish name ;) Essentially, we want it to do the following things:
|
||||
This tutorial will demonstrate a blogging application named Flaskr, but feel
|
||||
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
|
||||
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
|
||||
(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
|
||||
enough for an application of this size. For larger applications, however,
|
||||
SQLite3 will be used directly for this application because it's good enough
|
||||
for an application of this size. For larger applications, however,
|
||||
it makes a lot of sense to use `SQLAlchemy`_, as it handles database
|
||||
connections in a more intelligent way, allowing you to target different
|
||||
relational databases at once and more. You might also want to consider
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
Step 1: Database Schema
|
||||
=======================
|
||||
|
||||
First, we want to create the database schema. Only a single table is needed
|
||||
for this application and we only want to support SQLite, so creating the
|
||||
database schema is quite easy. Just put the following contents into a file
|
||||
named `schema.sql` in the just created `flaskr` folder:
|
||||
In this step, you will create the database schema. Only a single table is
|
||||
needed for this application and it will only support SQLite. All you need to do
|
||||
is put the following contents into a file named :file:`schema.sql` in the
|
||||
:file:`flaskr/flaskr` folder:
|
||||
|
||||
.. sourcecode:: sql
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ named `schema.sql` in the just created `flaskr` folder:
|
|||
'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
|
||||
automatically incrementing integer and a primary key, the other two are
|
||||
strings that must not be null.
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
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``. We will place this file inside the ``flaskr``
|
||||
folder. We will begin by adding the imports we need and by adding the config
|
||||
section. For small applications, it is possible to drop the configuration
|
||||
directly into the module, and this is what we will be doing here. However,
|
||||
a cleaner solution would be to create a separate ``.ini`` or ``.py`` file,
|
||||
load that, and import the values from there.
|
||||
Now that the schema is in place, you can create the application module,
|
||||
:file:`flaskr.py`. This file should be placed inside of the
|
||||
:file:`flaskr/flaskr` folder. The first several lines of code in the
|
||||
application module are the needed import statements. After that there will be a
|
||||
few lines of configuration code. For small applications like ``flaskr``, it is
|
||||
possible to drop the configuration directly into the module. However, a cleaner
|
||||
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
|
||||
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, \
|
||||
render_template, flash
|
||||
|
||||
Next, we can create our actual application and initialize it with the
|
||||
config from the same file in :file:`flaskr.py`::
|
||||
The next couple lines will create the actual application instance and
|
||||
initialize it with the config from the same file in :file:`flaskr.py`:
|
||||
|
||||
# create our little application :)
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(__name__)
|
||||
.. sourcecode:: python
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
The :class:`~flask.Config` object works similarly to a dictionary so we
|
||||
can update it with new values.
|
||||
The :class:`~flask.Config` object works similarly to a dictionary, so it can be
|
||||
updated with new values.
|
||||
|
||||
.. 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
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
.. sourcecode:: python
|
||||
|
||||
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.
|
||||
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
|
||||
specified database. This can be used to open a connection on request and
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
::
|
||||
.. sourcecode:: python
|
||||
|
||||
def connect_db():
|
||||
"""Connects to the specific database."""
|
||||
|
|
@ -90,29 +92,6 @@ tuples.
|
|||
rv.row_factory = sqlite3.Row
|
||||
return rv
|
||||
|
||||
With that out of the way, you should be able to start up the application
|
||||
without problems. Do this with the following commands::
|
||||
In the next section you will see how to run the application.
|
||||
|
||||
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
Normal file
89
docs/tutorial/setuptools.rst
Normal file
|
|
@ -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
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
.. _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, we would only get an exception that Flask cannot find the templates. The
|
||||
templates are using `Jinja2`_ syntax and have autoescaping enabled by
|
||||
Now it is time to start working on the templates. As you may have
|
||||
noticed, if you make requests with the app running, you will 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
|
||||
|
|
@ -57,9 +58,9 @@ show_entries.html
|
|||
|
||||
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
|
||||
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:
|
||||
in with the :func:`~flask.render_template` function. Notice that the form is
|
||||
configured to to submit to the `add_entry` view function and use ``POST`` as
|
||||
HTTP method:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
.. _tutorial-views:
|
||||
|
||||
Step 5: The View Functions
|
||||
Step 7: The View Functions
|
||||
==========================
|
||||
|
||||
Now that the database connections are working, we can start writing the
|
||||
view functions. We will need four of them:
|
||||
Now that the database connections are working, you can start writing the
|
||||
view functions. You will need four of them:
|
||||
|
||||
Show Entries
|
||||
------------
|
||||
|
|
@ -30,7 +30,7 @@ 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, we will
|
||||
`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::
|
||||
|
||||
|
|
@ -45,8 +45,8 @@ redirect back to the `show_entries` page::
|
|||
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``).
|
||||
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
|
||||
|
||||
|
|
@ -81,11 +81,11 @@ notified about that, and the user is asked again::
|
|||
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
|
||||
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 we don't have to check
|
||||
if the user was logged in.
|
||||
key is not in there. This is helpful because now it is not necessary to
|
||||
check if the user was logged in.
|
||||
|
||||
::
|
||||
|
||||
|
|
@ -94,11 +94,24 @@ if the user was logged in.
|
|||
session.pop('logged_in', None)
|
||||
flash('You were logged out')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
||||
Note that it is not a good idea to store passwords in plain text. You want to
|
||||
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
|
||||
password. However, the emphasis of this tutorial is to demonstrate the basics
|
||||
of Flask and plain text passwords are used for simplicity.
|
||||
|
||||
.. 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/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue