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
...
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:
Here 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
...
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 Becoming Big section for some inspiration how to deal with that.
For larger applications with more than a dozen views it makes sense to split the views into module. First let’s look at the typical struture 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 Module object with the name of the package. This works very similar to the Flask object you have already worked with, it just does not support all of the method, but most of them are the same.
Long story short, here 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 login():
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
from yourapplication.views.admin import admin
from yourapplication.views.frontend import frontend
app = Flask(__name__)
app.register_module(admin)
app.register_module(frontend)
So what is different when working with modules? It mainly affects URL generation. Remember the 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 module 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"