forked from orbit-oss/flask
Proofreading the documentation
This commit is contained in:
parent
4ff9493e57
commit
a7ff9dbddd
8 changed files with 58 additions and 58 deletions
|
|
@ -8,11 +8,11 @@ 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.
|
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
|
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 bringing those
|
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.
|
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
|
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
|
to take the code from Flask and put it into a new module within the
|
||||||
applications and expanding on that.
|
applications and expand on that.
|
||||||
|
|
||||||
What Could Be Improved?
|
What Could Be Improved?
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
@ -20,12 +20,12 @@ What Could Be Improved?
|
||||||
For instance it makes a lot of sense to change the way endpoints (the
|
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
|
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
|
name into account. Right now the function name is the URL name, but
|
||||||
imagine you have a large applications consisting of multiple components.
|
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
|
In that case, it makes a lot of sense to use dotted names for the URL
|
||||||
endpoints.
|
endpoints.
|
||||||
|
|
||||||
Here some suggestions how Flask can be modified to better accomodate large
|
Here are some suggestions for how Flask can be modified to better
|
||||||
scale applications:
|
accomodate large-scale applications:
|
||||||
|
|
||||||
- implement dotted names for URL endpoints
|
- implement dotted names for URL endpoints
|
||||||
- get rid of the decorator function registering which causes a lot
|
- get rid of the decorator function registering which causes a lot
|
||||||
|
|
@ -35,7 +35,7 @@ scale applications:
|
||||||
better solution would be to have one module with all URLs in there and
|
better solution would be to have one module with all URLs in there and
|
||||||
specifing the target functions explicitly or by name and importing
|
specifing the target functions explicitly or by name and importing
|
||||||
them when needed.
|
them when needed.
|
||||||
- switch to explicit request object passing. This makes it more to type
|
- switch to explicit request object passing. This requires more typing
|
||||||
(because you now have something to pass around) but it makes it a
|
(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.
|
whole lot easier to debug hairy situations and to test the code.
|
||||||
- integrate the `Babel`_ i18n package or `SQLAlchemy`_ directly into the
|
- integrate the `Babel`_ i18n package or `SQLAlchemy`_ directly into the
|
||||||
|
|
@ -44,14 +44,14 @@ scale applications:
|
||||||
.. _Babel: http://babel.edgewall.org/
|
.. _Babel: http://babel.edgewall.org/
|
||||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||||
|
|
||||||
Why does not Flask do all that by Default?
|
Why does Flask not do all that by Default?
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
There is a huge difference between a small application that only has to
|
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
|
handle a couple of requests per second and with an overall code complexity
|
||||||
of less than 4000 lines of code or something of larger scale. At one
|
of less than 4000 lines of code and something of larger scale. At some
|
||||||
point it becomes important to integrate external systems, different
|
point it becomes important to integrate external systems, different
|
||||||
storage backends and more.
|
storage backends and more.
|
||||||
|
|
||||||
If Flask was designed with all these contingencies in mind, it would be a
|
If Flask was designed with all these contingencies in mind, it would be a
|
||||||
much more complex framework and less easy to get started with.
|
much more complex framework and harder to get started with.
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ Installing `mod_wsgi`
|
||||||
If you don't have `mod_wsgi` installed yet you have to either install it using
|
If you don't have `mod_wsgi` installed yet you have to either install it using
|
||||||
a package manager or compile it yourself.
|
a package manager or compile it yourself.
|
||||||
|
|
||||||
The mod_wsgi `installation instructions`_ cover installation instructions for
|
The mod_wsgi `installation instructions`_ cover source installations on UNIX
|
||||||
source installations on UNIX systems.
|
systems.
|
||||||
|
|
||||||
If you are using ubuntu / debian you can apt-get it and activate it as follows::
|
If you are using ubuntu / debian you can apt-get it and activate it as follows::
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ For most applications the following file should be sufficient::
|
||||||
If you don't have a factory function for application creation but a singleton
|
If you don't have a factory function for application creation but a singleton
|
||||||
instance you can directly import that one as `application`.
|
instance you can directly import that one as `application`.
|
||||||
|
|
||||||
Store that file somewhere where you will find it again (eg:
|
Store that file somewhere that you will find it again (e.g.:
|
||||||
`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
`/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
|
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.
|
want to install it system wide consider using a `virtual python`_ instance.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ Design Decisions in Flask
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
If you are curious why Flask does certain things the way it does and not
|
If you are curious why Flask does certain things the way it does and not
|
||||||
different, this section is for you. This should give you an idea about
|
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
|
some of the design decisions that may appear arbitrary and surprising at
|
||||||
first, especially in direct comparison with other frameworks.
|
first, especially in direct comparison with other frameworks.
|
||||||
|
|
||||||
|
|
@ -44,10 +44,10 @@ something it can be very helpful to create a minimal application to test
|
||||||
specific behavior. When the application object is deleted everything it
|
specific behavior. When the application object is deleted everything it
|
||||||
allocated will be freed again.
|
allocated will be freed again.
|
||||||
|
|
||||||
Another thing that becomes possible with having an explicit object laying
|
Another thing that becomes possible when you have an explicit object laying
|
||||||
around in your code is that you can subclass the base class
|
around in your code is that you can subclass the base class
|
||||||
(:class:`~flask.Flask`) to alter specific behaviour. This would not be
|
(:class:`~flask.Flask`) to alter specific behaviour. This would not be
|
||||||
possible without hacks if the object was created ahead of time for you
|
possible without hacks if the object were created ahead of time for you
|
||||||
based on a class that is not exposed to you.
|
based on a class that is not exposed to you.
|
||||||
|
|
||||||
But there is another very important reason why Flask depends on an
|
But there is another very important reason why Flask depends on an
|
||||||
|
|
@ -83,23 +83,23 @@ that limitation that Jinja2 is *always* configured will probably go away,
|
||||||
the decision to bundle one template engine and use that will not.
|
the decision to bundle one template engine and use that will not.
|
||||||
|
|
||||||
Template engines are like programming languages and each of those engines
|
Template engines are like programming languages and each of those engines
|
||||||
has a certain understandment about how things work. On the surface they
|
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
|
all work the same: you tell the engine to evaluate a template with a set
|
||||||
of variables and take the return value as string.
|
of variables and take the return value as string.
|
||||||
|
|
||||||
But that's about where similarities end. Jinja2 for example has an
|
But that's about where similarities end. Jinja2 for example has an
|
||||||
extensive filter system, a certain way to do template inheritance, support
|
extensive filter system, a certain way to do template inheritance, support
|
||||||
for reusable blocks (macros) that can be used from inside templates and
|
for reusable blocks (macros) that can be used from inside templates and
|
||||||
also from Python code, is using unicode for all operations, supports
|
also from Python code, uses unicode for all operations, supports
|
||||||
iterative template rendering, configurable syntax and more. On the other
|
iterative template rendering, configurable syntax and more. On the other
|
||||||
hand an engine like Genshi is based on XML stream evaluation, template
|
hand an engine like Genshi is based on XML stream evaluation, template
|
||||||
inheritance by taking the availability of XPath into account and more.
|
inheritance by taking the availability of XPath into account and more.
|
||||||
Mako on the other hand treats templates similar to Python modules.
|
Mako on the other hand treats templates similar to Python modules.
|
||||||
|
|
||||||
When it comes to bridge a template engine with an application or framework
|
When it comes to connecting a template engine with an application or
|
||||||
there is more than just rendering templates. Flask uses Jinja2's
|
framework there is more than just rendering templates. For instance,
|
||||||
extensive autoescaping support for instance. Also it provides ways to
|
Flask uses Jinja2's extensive autoescaping support. Also it provides
|
||||||
access macros from Jinja2 templates.
|
ways to access macros from Jinja2 templates.
|
||||||
|
|
||||||
A template abstraction layer that would not take the unique features of
|
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
|
the template engines away is a science on its own and a too large
|
||||||
|
|
@ -115,9 +115,9 @@ 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
|
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
|
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
|
applications in Ruby land do not work with Rack directly, but on top of a
|
||||||
lirbary with the same name. This Rack library has two equivalents in
|
library with the same name. This Rack library has two equivalents in
|
||||||
Python: WebOb (formerly Paste) and Werkzeug. Paste is still around but
|
Python: WebOb (formerly Paste) and Werkzeug. Paste is still around but
|
||||||
from my understanding it's sortof deprecated in favour of WebOb. The
|
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
|
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
|
in mind: be a good implementation of WSGI for other applications to take
|
||||||
advantage.
|
advantage.
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ way and why there are multiple ways.
|
||||||
|
|
||||||
Flask depends on two external libraries: `Werkzeug
|
Flask depends on two external libraries: `Werkzeug
|
||||||
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/2/>`_.
|
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/2/>`_.
|
||||||
The first on is responsible for interfacing WSGI the latter to render
|
The first one is responsible for interfacing WSGI the latter for rendering
|
||||||
templates. Now you are maybe asking, what is WSGI? WSGI is a standard
|
templates. Now you are maybe asking, what is WSGI? WSGI is a standard
|
||||||
in Python that is basically responsible for ensuring that your application
|
in Python that is basically responsible for ensuring that your application
|
||||||
is behaving in a specific way that you can run it on different
|
is behaving in a specific way so that you can run it on different
|
||||||
environments (for example on a local development server, on an Apache2, on
|
environments (for example on a local development server, on an Apache2, on
|
||||||
lighttpd, on Google's App Engine or whatever you have in mind).
|
lighttpd, on Google's App Engine or whatever you have in mind).
|
||||||
|
|
||||||
|
|
@ -26,10 +26,10 @@ 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
|
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
|
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
|
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
|
that you will be working with different versions of Python itself or at
|
||||||
library involved. Because let's face it: quite often libraries break
|
least an individual library. Because let's face it: quite often libraries
|
||||||
backwards compatibility and it's unlikely that your application will
|
break backwards compatibility and it's unlikely that your application will
|
||||||
not have any dependencies, that just won't happen. So virtualenv for the
|
not have any dependencies, that just won't happen. So virtualenv to the
|
||||||
rescue!
|
rescue!
|
||||||
|
|
||||||
It basically makes it possible to have multiple side-by-side
|
It basically makes it possible to have multiple side-by-side
|
||||||
|
|
@ -47,7 +47,7 @@ or even better::
|
||||||
|
|
||||||
$ sudo pip install virtualenv
|
$ sudo pip install virtualenv
|
||||||
|
|
||||||
Changes are you have virtualenv installed on your system then. Maybe it's
|
Chances are you have virtualenv installed on your system then. Maybe it's
|
||||||
even in your package manager (on ubuntu try ``sudo apt-get install
|
even in your package manager (on ubuntu try ``sudo apt-get install
|
||||||
python-virtualenv``).
|
python-virtualenv``).
|
||||||
|
|
||||||
|
|
@ -152,7 +152,7 @@ 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
|
and other Python scripts to the path. To do that you have to add the
|
||||||
Python installation's Script folder to the `PATH` variable.
|
Python installation's Script folder to the `PATH` variable.
|
||||||
|
|
||||||
To do that, click right on your "Computer" desktop icon and click
|
To do that, right-click on your "Computer" desktop icon and click
|
||||||
"Properties". On Windows Vista and Windows 7 then click on "Advanced System
|
"Properties". On Windows Vista and Windows 7 then click on "Advanced System
|
||||||
settings", on Windows XP click on the "Advanced" tab instead. Then click
|
settings", on Windows XP click on the "Advanced" tab instead. Then click
|
||||||
on the "Environment variables" button and double click on the "Path"
|
on the "Environment variables" button and double click on the "Path"
|
||||||
|
|
@ -165,7 +165,7 @@ the following value::
|
||||||
|
|
||||||
;C:\Python26\Scripts
|
;C:\Python26\Scripts
|
||||||
|
|
||||||
Then you are done. To check if it worked, open the cmd and execute
|
Then you are done. To check that it worked, open the cmd and execute
|
||||||
"easy_install". If you have UAC enabled it should prompt you for admin
|
"easy_install". If you have UAC enabled it should prompt you for admin
|
||||||
privileges.
|
privileges.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ 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
|
into multiple modules. The only thing you have to remember is the
|
||||||
following quick checklist:
|
following quick checklist:
|
||||||
|
|
||||||
1. the `Flask` application object creation have to be in the
|
1. the `Flask` application object creation has to be in the
|
||||||
`__init__.py` file. That way each module can import it safely and the
|
`__init__.py` file. That way each module can import it safely and the
|
||||||
`__name__` variable will resole to the correct package.
|
`__name__` variable will resolve to the correct package.
|
||||||
2. all the view functions (the ones with a :meth:`~flask.Flask.route`
|
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.
|
decorator on top) have to be imported when in the `__init__.py` file.
|
||||||
Not the objects itself, but the module it is in. Do the importing at
|
Not the object itself, but the module it is in. Do the importing at
|
||||||
the *bottom* of the file.
|
the *bottom* of the file.
|
||||||
|
|
||||||
Here an example `__init__.py`::
|
Here an example `__init__.py`::
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ SQLAlchemy in Flask
|
||||||
Many people prefer `SQLAlchemy`_ for database access. In this case it's
|
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
|
encouraged to use a package instead of a module for your flask application
|
||||||
and drop the models into a separate module (:ref:`larger-applications`).
|
and drop the models into a separate module (:ref:`larger-applications`).
|
||||||
Although that is not necessary but makes a lot of sense.
|
While that is not necessary, it makes a lot of sense.
|
||||||
|
|
||||||
There are three very common ways to use SQLAlchemy. I will outline each
|
There are three very common ways to use SQLAlchemy. I will outline each
|
||||||
of them here:
|
of them here:
|
||||||
|
|
@ -52,7 +52,7 @@ automatically remove database sessions at the end of the request for you::
|
||||||
db_session.remove()
|
db_session.remove()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
Here an example model (put that into `models.py` for instance)::
|
Here is an example model (put this into `models.py`, e.g.)::
|
||||||
|
|
||||||
from sqlalchemy import Column, Integer, String
|
from sqlalchemy import Column, Integer, String
|
||||||
from yourapplication.database import Base
|
from yourapplication.database import Base
|
||||||
|
|
@ -70,7 +70,7 @@ Here an example model (put that into `models.py` for instance)::
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User %r>' % (self.name, self.email)
|
return '<User %r>' % (self.name, self.email)
|
||||||
|
|
||||||
You can insert entries into the database like this then:
|
You can insert entries into the database like this:
|
||||||
|
|
||||||
>>> from yourapplication.database import db_session
|
>>> from yourapplication.database import db_session
|
||||||
>>> from yourapplication.models import User
|
>>> from yourapplication.models import User
|
||||||
|
|
@ -95,11 +95,11 @@ Manual Object Relational Mapping
|
||||||
Manual object relational mapping has a few upsides and a few downsides
|
Manual object relational mapping has a few upsides and a few downsides
|
||||||
versus the declarative approach from above. The main difference is that
|
versus the declarative approach from above. The main difference is that
|
||||||
you define tables and classes separately and map them together. It's more
|
you define tables and classes separately and map them together. It's more
|
||||||
flexible but a little more to type. In general it works similar to the
|
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
|
declarative approach, so make sure to also split up your application into
|
||||||
multiple modules in a package.
|
multiple modules in a package.
|
||||||
|
|
||||||
Here the example `database.py` module for your application::
|
Here is an example `database.py` module for your application::
|
||||||
|
|
||||||
from sqlalchemy import create_engine, MetaData
|
from sqlalchemy import create_engine, MetaData
|
||||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
|
@ -112,7 +112,7 @@ Here the example `database.py` module for your application::
|
||||||
def init_db():
|
def init_db():
|
||||||
metadata.create_all(bind=engine)
|
metadata.create_all(bind=engine)
|
||||||
|
|
||||||
As for the declarative approach you need to close down the session after
|
As for the declarative approach you need to close the session after
|
||||||
each request. Put this into your application module::
|
each request. Put this into your application module::
|
||||||
|
|
||||||
from yourapplication.database import db_session
|
from yourapplication.database import db_session
|
||||||
|
|
@ -122,7 +122,7 @@ each request. Put this into your application module::
|
||||||
db_session.remove()
|
db_session.remove()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
Here an example table and model (put that into `models.py` for instance)::
|
Here is an example table and model (put this into `models.py`)::
|
||||||
|
|
||||||
from sqlalchemy import Table, Column, Integer, String
|
from sqlalchemy import Table, Column, Integer, String
|
||||||
from sqlalchemy.orm import mapper
|
from sqlalchemy.orm import mapper
|
||||||
|
|
@ -172,7 +172,7 @@ connection first so that we can use a transaction:
|
||||||
|
|
||||||
SQLAlchemy will automatically commit for us.
|
SQLAlchemy will automatically commit for us.
|
||||||
|
|
||||||
To query your database, yu use the engine directly or use a connection:
|
To query your database, you use the engine directly or use a connection:
|
||||||
|
|
||||||
>>> users.select(users.c.id == 1).execute().first()
|
>>> users.select(users.c.id == 1).execute().first()
|
||||||
(1, u'admin', u'admin@localhost')
|
(1, u'admin', u'admin@localhost')
|
||||||
|
|
@ -183,7 +183,7 @@ These results are also dict-like tuples:
|
||||||
>>> r['name']
|
>>> r['name']
|
||||||
u'admin'
|
u'admin'
|
||||||
|
|
||||||
You can also pass string of SQL statements to the
|
You can also pass strings of SQL statements to the
|
||||||
:meth:`~sqlalchemy.engine.base.Connection.execute` method:
|
:meth:`~sqlalchemy.engine.base.Connection.execute` method:
|
||||||
|
|
||||||
>>> engine.execute('select * from users where id = :1', [1]).first()
|
>>> engine.execute('select * from users where id = :1', [1]).first()
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
Using SQLite 3 with Flask
|
Using SQLite 3 with Flask
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
In Flask you can implement opening of dabase connections at the beginning
|
In Flask you can implement opening of database connections at the beginning
|
||||||
of the request and closing at the end with the
|
of the request and closing at the end with the
|
||||||
:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request`
|
:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request`
|
||||||
decorators in combination with the special :class:`~flask.g` object.
|
decorators in combination with the special :class:`~flask.g` object.
|
||||||
|
|
||||||
So here a simple example how you can use SQLite 3 with Flask::
|
So here a simple example of how you can use SQLite 3 with Flask::
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from flask import g
|
from flask import g
|
||||||
|
|
@ -70,7 +70,7 @@ Initial Schemas
|
||||||
|
|
||||||
Relational databases need schemas, so applications often ship a
|
Relational databases need schemas, so applications often ship a
|
||||||
`schema.sql` file that creates the database. It's a good idea to provide
|
`schema.sql` file that creates the database. It's a good idea to provide
|
||||||
a function that creates the database bases on that schema. This function
|
a function that creates the database based on that schema. This function
|
||||||
can do that for you::
|
can do that for you::
|
||||||
|
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ Testing Flask Applications
|
||||||
Not sure where that is coming from, and it's not entirely correct, but
|
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
|
also not that far from the truth. Untested applications make it hard to
|
||||||
improve existing code and developers of untested applications tend to
|
improve existing code and developers of untested applications tend to
|
||||||
become pretty paranoid. If an application however has automated tests you
|
become pretty paranoid. If an application however has automated tests, you
|
||||||
can savely change things and you will instantly know if your change broke
|
can safely change things and you will instantly know if your change broke
|
||||||
something.
|
something.
|
||||||
|
|
||||||
Flask gives you a couple of ways to test applications. It mainly does
|
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
|
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
|
code and handling the context locals for you. You can then use that with
|
||||||
your favourite testing solution. In this documentation we will us the
|
your favourite testing solution. In this documentation we will use the
|
||||||
:mod:`unittest` package that comes preinstalled with each Python
|
:mod:`unittest` package that comes preinstalled with each Python
|
||||||
installation.
|
installation.
|
||||||
|
|
||||||
|
|
@ -50,13 +50,13 @@ In order to test that, we add a second module (
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
The code in the `setUp` function creates a new test client and initialize
|
The code in the `setUp` function creates a new test client and initializes
|
||||||
a new database. That function is called before each individual test function.
|
a new database. That function is called before each individual test function.
|
||||||
What the test client does for us is giving us a simple interface to the
|
What the test client does is give us a simple interface to the
|
||||||
application. We can trigger test requests to the application and the
|
application. We can trigger test requests to the application and the
|
||||||
client will also keep track of cookies for us.
|
client will also keep track of cookies for us.
|
||||||
|
|
||||||
Because SQLite3 is filesystem based we can easily use the tempfile module
|
Because SQLite3 is filesystem-based we can easily use the tempfile module
|
||||||
to create a temporary database and initialize it. Just make sure that you
|
to create a temporary database and initialize it. Just make sure that you
|
||||||
keep a reference to the :class:`~tempfile.NamedTemporaryFile` around (we
|
keep a reference to the :class:`~tempfile.NamedTemporaryFile` around (we
|
||||||
store it as `self.db` because of that) so that the garbage collector does
|
store it as `self.db` because of that) so that the garbage collector does
|
||||||
|
|
@ -112,20 +112,20 @@ Run it again and you should see one passing test::
|
||||||
|
|
||||||
OK
|
OK
|
||||||
|
|
||||||
Of course you can submit forms with the test client as well which we will
|
Of course you can submit forms with the test client as well, which we will
|
||||||
use now to log our user in.
|
use now to log our user in.
|
||||||
|
|
||||||
Logging In and Out
|
Logging In and Out
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The majority of the functionality of our application is only available for
|
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 into the
|
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
|
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
|
login and logout pages with the required form data (username and
|
||||||
password). Because the login and logout pages redirect, we tell the
|
password). Because the login and logout pages redirect, we tell the
|
||||||
client to `follow_redirects`.
|
client to `follow_redirects`.
|
||||||
|
|
||||||
Add the following two methods do your `FlaskrTestCase` class::
|
Add the following two methods to your `FlaskrTestCase` class::
|
||||||
|
|
||||||
def login(self, username, password):
|
def login(self, username, password):
|
||||||
return self.app.post('/login', data=dict(
|
return self.app.post('/login', data=dict(
|
||||||
|
|
@ -137,7 +137,7 @@ Add the following two methods do your `FlaskrTestCase` class::
|
||||||
return self.app.get('/logout', follow_redirects=True)
|
return self.app.get('/logout', follow_redirects=True)
|
||||||
|
|
||||||
Now we can easily test if logging in and out works and that it fails with
|
Now we can easily test if logging in and out works and that it fails with
|
||||||
invalid credentials. Add this as new test to the class::
|
invalid credentials. Add this new test to the class::
|
||||||
|
|
||||||
def test_login_logout(self):
|
def test_login_logout(self):
|
||||||
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD)
|
rv = self.login(flaskr.USERNAME, flaskr.PASSWORD)
|
||||||
|
|
@ -165,7 +165,7 @@ like this::
|
||||||
assert '<Hello>' in rv.data
|
assert '<Hello>' in rv.data
|
||||||
assert '<strong>HTML</strong> allowed here' in rv.data
|
assert '<strong>HTML</strong> allowed here' in rv.data
|
||||||
|
|
||||||
Here we also check that HTML is allowed in the text but not in the title
|
Here we check that HTML is allowed in the text but not in the title,
|
||||||
which is the intended behavior.
|
which is the intended behavior.
|
||||||
|
|
||||||
Running that should now give us three passing tests::
|
Running that should now give us three passing tests::
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue