|
|
|
.. _larger-applications:
|
|
|
|
|
|
|
|
Larger Applications
|
|
|
|
===================
|
|
|
|
|
|
|
|
For larger applications it's a good idea to use a package instead of a
|
|
|
|
module. That is quite simple. Imagine a small application looks like
|
|
|
|
this::
|
|
|
|
|
|
|
|
/yourapplication
|
|
|
|
/yourapplication.py
|
|
|
|
/static
|
|
|
|
/style.css
|
|
|
|
/templates
|
|
|
|
layout.html
|
|
|
|
index.html
|
|
|
|
login.html
|
|
|
|
...
|
|
|
|
|
|
|
|
Simple Packages
|
|
|
|
---------------
|
|
|
|
|
|
|
|
To convert that into a larger one, just create a new folder
|
|
|
|
`yourapplication` inside the existing one and move everything below it.
|
|
|
|
Then rename `yourapplication.py` to `__init__.py`. (Make sure to delete
|
|
|
|
all `.pyc` files first, otherwise things would most likely break)
|
|
|
|
|
|
|
|
You should then end up with something like that::
|
|
|
|
|
|
|
|
/yourapplication
|
|
|
|
/yourapplication
|
|
|
|
/__init__.py
|
|
|
|
/static
|
|
|
|
/style.css
|
|
|
|
/templates
|
|
|
|
layout.html
|
|
|
|
index.html
|
|
|
|
login.html
|
|
|
|
...
|
|
|
|
|
|
|
|
But how do you run your application now? The naive ``python
|
|
|
|
yourapplication/__init__.py`` will not work. Let's just say that Python
|
|
|
|
does not want modules in packages to be the startup file. But that is not
|
|
|
|
a big problem, just add a new file called `runserver.py` next to the inner
|
|
|
|
`yourapplication` folder with the following contents::
|
|
|
|
|
|
|
|
from yourapplication import app
|
|
|
|
app.run(debug=True)
|
|
|
|
|
|
|
|
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
|
|
|
|
following quick checklist:
|
|
|
|
|
|
|
|
1. the `Flask` application object creation has to be in the
|
|
|
|
`__init__.py` file. That way each module can import it safely and the
|
|
|
|
`__name__` variable will resolve to the correct package.
|
|
|
|
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.
|
|
|
|
Not the object itself, but the module it is in. Import the view module
|
|
|
|
*after the application object is created*.
|
|
|
|
|
|
|
|
Here's an example `__init__.py`::
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
import yourapplication.views
|
|
|
|
|
|
|
|
And this is what `views.py` would look like::
|
|
|
|
|
|
|
|
from yourapplication import app
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Hello World!'
|
|
|
|
|
|
|
|
You should then end up with something like that::
|
|
|
|
|
|
|
|
/yourapplication
|
|
|
|
/yourapplication
|
|
|
|
/__init__.py
|
|
|
|
/views.py
|
|
|
|
/static
|
|
|
|
/style.css
|
|
|
|
/templates
|
|
|
|
layout.html
|
|
|
|
index.html
|
|
|
|
login.html
|
|
|
|
...
|
|
|
|
|
|
|
|
.. admonition:: Circular Imports
|
|
|
|
|
|
|
|
Every Python programmer hates them, and yet we just added some:
|
|
|
|
circular imports (That's when two modules depend on each other. In this
|
|
|
|
case `views.py` depends on `__init__.py`). Be advised that this is a
|
|
|
|
bad idea in general but here it is actually fine. The reason for this is
|
|
|
|
that we are not actually using the views in `__init__.py` and just
|
|
|
|
ensuring the module is imported and we are doing that at the bottom of
|
|
|
|
the file.
|
|
|
|
|
|
|
|
There are still some problems with that approach but if you want to use
|
|
|
|
decorators there is no way around that. Check out the
|
|
|
|
:ref:`becomingbig` section for some inspiration how to deal with that.
|
|
|
|
|
|
|
|
|
|
|
|
.. _working-with-modules:
|
|
|
|
|
|
|
|
Working with Modules
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
For larger applications with more than a dozen views it makes sense to
|
|
|
|
split the views into modules. First let's look at the typical structure of
|
|
|
|
such an application::
|
|
|
|
|
|
|
|
/yourapplication
|
|
|
|
/yourapplication
|
|
|
|
/__init__.py
|
|
|
|
/views
|
|
|
|
__init__.py
|
|
|
|
admin.py
|
|
|
|
frontend.py
|
|
|
|
/static
|
|
|
|
/style.css
|
|
|
|
/templates
|
|
|
|
layout.html
|
|
|
|
index.html
|
|
|
|
login.html
|
|
|
|
...
|
|
|
|
|
|
|
|
The views are stored in the `yourapplication.views` package. Just make
|
|
|
|
sure to place an empty `__init__.py` file in there. Let's start with the
|
|
|
|
`admin.py` file in the view package.
|
|
|
|
|
|
|
|
First we have to create a :class:`~flask.Module` object with the name of
|
|
|
|
the package. This works very similar to the :class:`~flask.Flask` object
|
|
|
|
you have already worked with, it just does not support all of the methods,
|
|
|
|
but most of them are the same.
|
|
|
|
|
|
|
|
Long story short, here's a nice and concise example::
|
|
|
|
|
|
|
|
from flask import Module
|
|
|
|
|
|
|
|
admin = Module(__name__)
|
|
|
|
|
|
|
|
@admin.route('/')
|
|
|
|
def index():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@admin.route('/login')
|
|
|
|
def login():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@admin.route('/logout')
|
|
|
|
def logout():
|
|
|
|
pass
|
|
|
|
|
|
|
|
Do the same with the `frontend.py` and then make sure to register the
|
|
|
|
modules in the application (`__init__.py`) like this::
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
from yourapplication.views.admin import admin
|
|
|
|
from yourapplication.views.frontend import frontend
|
|
|
|
app.register_module(admin, url_prefix='/admin')
|
|
|
|
app.register_module(frontend)
|
|
|
|
|
|
|
|
We register the modules with the app so that it can add them to the
|
|
|
|
URL map for our application. Note the prefix argument to the admin
|
|
|
|
module: by default when we register a module, that module's end-points
|
|
|
|
will be registered on `/` unless we specify this argument.
|
|
|
|
|
|
|
|
So what is different when working with modules? It mainly affects URL
|
|
|
|
generation. Remember the :func:`~flask.url_for` function? When not
|
|
|
|
working with modules it accepts the name of the function as first
|
|
|
|
argument. This first argument is called the "endpoint". When you are
|
|
|
|
working with modules you can use the name of the function like you did
|
|
|
|
without, when generating modules from a function or template in the same
|
|
|
|
module. If you want to generate the URL to another module, prefix it with
|
|
|
|
the name of the module and a dot.
|
|
|
|
|
|
|
|
Confused? Let's clear that up with some examples. Imagine you have a
|
|
|
|
method in one module (say `admin`) and you want to redirect to a
|
|
|
|
different module (say `frontend`). This would look like this::
|
|
|
|
|
|
|
|
@admin.route('/to_frontend')
|
|
|
|
def to_frontend():
|
|
|
|
return redirect(url_for('frontend.index'))
|
|
|
|
|
|
|
|
@frontend.route('/')
|
|
|
|
def index():
|
|
|
|
return "I'm the frontend index"
|
|
|
|
|
|
|
|
Now let's say we only want to redirect to a different function in the same
|
|
|
|
module. Then we can either use the full qualified endpoint name like we
|
|
|
|
did in the example above, or we just use the function name::
|
|
|
|
|
|
|
|
@frontend.route('/to_index')
|
|
|
|
def to_index():
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
@frontend.route('/')
|
|
|
|
def index():
|
|
|
|
return "I'm the index"
|
|
|
|
|
|
|
|
.. _modules-and-resources:
|
|
|
|
|
|
|
|
Modules and Resources
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
.. versionadded:: 0.5
|
|
|
|
|
|
|
|
If a module is located inside an actual Python package it may contain
|
|
|
|
static files and templates. Imagine you have an application like this::
|
|
|
|
|
|
|
|
|
|
|
|
/yourapplication
|
|
|
|
__init__.py
|
|
|
|
/apps
|
|
|
|
__init__.py
|
|
|
|
/frontend
|
|
|
|
__init__.py
|
|
|
|
views.py
|
|
|
|
/static
|
|
|
|
style.css
|
|
|
|
/templates
|
|
|
|
index.html
|
|
|
|
about.html
|
|
|
|
...
|
|
|
|
/admin
|
|
|
|
__init__.py
|
|
|
|
views.py
|
|
|
|
/static
|
|
|
|
style.css
|
|
|
|
/templates
|
|
|
|
list_items.html
|
|
|
|
show_item.html
|
|
|
|
...
|
|
|
|
|
|
|
|
The static folders automatically become exposed as URLs. For example if
|
|
|
|
the `admin` module is exported with an URL prefix of ``/admin`` you can
|
|
|
|
access the style css from its static folder by going to
|
|
|
|
``/admin/static/style.css``. The URL endpoint for the static files of the
|
|
|
|
admin would be ``'admin.static'``, similar to how you refer to the regular
|
|
|
|
static folder of the whole application as ``'static'``.
|
|
|
|
|
|
|
|
If you want to refer to the templates you just have to prefix it with the
|
|
|
|
name of the module. So for the admin it would be
|
|
|
|
``render_template('admin/list_items.html')`` and so on. It is not
|
|
|
|
possible to refer to templates without the prefixed module name. This is
|
|
|
|
explicit unlike URL rules. Also with the move of the views into from
|
|
|
|
`yourapplication.views.admin` too `yourapplication.apps.admin.views` you
|
|
|
|
will have to give the module an explit shortname. Why? Because otherwise
|
|
|
|
all your modules will be internally known as `views` which is obviously
|
|
|
|
not what you want::
|
|
|
|
|
|
|
|
# in yourapplication/apps/admin/views.py
|
|
|
|
admin = Module(__name__, 'admin')
|
|
|
|
|
|
|
|
The setup code changes slightly because of the imports::
|
|
|
|
|
|
|
|
# in yourapplication/__init__.py
|
|
|
|
from flask import Flask
|
|
|
|
from yourapplication.apps.admin.views import admin
|
|
|
|
from yourapplication.apps.frontend.views import frontend
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
app.register_module(admin, url_prefix='/admin')
|
|
|
|
app.register_module(frontend)
|
|
|
|
|
|
|
|
.. admonition:: References to Static Folders
|
|
|
|
|
|
|
|
Please keep in mind that if you are using unqualified endpoints by
|
|
|
|
default Flask will always assume the module's static folder, even if
|
|
|
|
there is no such folder.
|
|
|
|
|
|
|
|
If you want to refer to the application's static folder, use a leading
|
|
|
|
dot::
|
|
|
|
|
|
|
|
# this refers to the application's static folder
|
|
|
|
url_for('.static', filename='static.css')
|
|
|
|
|
|
|
|
# this refers to the current module's static folder
|
|
|
|
url_for('static', filename='static.css')
|
|
|
|
|
|
|
|
This is the case for all endpoints, not just static folders, but for
|
|
|
|
static folders it's more common that you will stumble upon this because
|
|
|
|
most applications will have a static folder in the application and not
|
|
|
|
a specific module.
|