View functions are the main part of a Flask application. If we keep adding them to the same file, we quickly will be unable to maintain our application. Luckily for us, Flask comes with a solution to that problem called Blueprint.
This post is part of my journey to learn Python. You can find the other parts of this series here.
What is the problem?
If we keep adding functions to the application file, it gets harder and harder to find the function we need. Working with a smaller file where all functions belong to the same concept or topic is much simpler.
If we move all the view functions for the contact form to a file /views/contact_views.py, we make a big step into a better structured application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from flask import render_template, request from hello_flask.viewmodels.contact_viewmodel import ContactViewModel @app.route('/contact', methods=['GET']) def contact_form(): return render_template('contact.html') @app.route('/contact', methods=['POST']) def contact_post(): vm = ContactViewModel() vm.validate() if vm.error: return render_template('contact.html', **vm.to_dict()) print(vm) resp = flask.redirect('/thanks') return resp @app.route('/thanks') def thanks(): return render_template('thankyou.html') |
However, if we now run our application and go to the /contacts
page, we get a 404 error:
Just moving view functions around is unfortunately not enough. Flask now no longer finds our functions because it does not know of their existence.
Blueprint to the rescue
Flask offers us with Blueprint an out-of-the-box solution to organise our view functions. This feature will do all the heavy lifting and we can concentrate on a few little changes that will glue everything together.
Create a Blueprint and use it
First, we need to create a Blueprint for our view file /views/contact_views.py. For that we need to give it a name, tell it in which file the functions are and the name of our templates folder:
1 2 3 |
import flask blueprint = flask.Blueprint('contact', __name__, template_folder='templates') |
In the same file we replace all calls to @app with @blueprint:
1 2 |
# Old: @app.route('/contact', methods=['GET']) @blueprint.route('/contact', methods=['GET']) |
The full /views/contact_views.py looks at the end like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import flask from flask import render_template, request from hello_flask.viewmodels.contact_viewmodel import ContactViewModel blueprint = flask.Blueprint('contact', __name__, template_folder='templates') @blueprint.route('/contact', methods=['GET']) def contact_form(): return render_template('contact/contact.html') @blueprint.route('/contact', methods=['POST']) def contact_post(): vm = ContactViewModel() vm.validate() if vm.error: return render_template('contact/contact.html', **vm.to_dict()) print(vm) resp = flask.redirect('/thanks') return resp @blueprint.route('/thanks') def thanks(): return render_template('contact/thankyou.html') |
Register the Blueprint
In our file app.py (where we have our Flask application) we need to register our blueprint.
1 2 3 |
def register_blueprints(): from hello_flask.views import contact_views app.register_blueprint(contact_views.blueprint) |
Depending on how you start your Flask app, you need to call this method from different places. I want to run my application from the command line and from VS Code and ended up with this little hack:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def main(): register_blueprints() app.run() def register_blueprints(): from hello_flask.views import contact_views app.register_blueprint(contact_views.blueprint) if(__name__ == "__main__"): print("start application") main() else: register_blueprints() |
If you start your application, you may end up with an error about Python being unable to load your module. In this case you can add these lines on top of your app.py file to add your application folder to the sys.path:
1 2 3 4 |
import os import sys folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, folder) |
The result
Moving all the view functions for the contact form to its own file improves the structure of my Flask application. I now no longer need to go through the whole application to find the parts for the contact form – instead I open the file with the appropriate name in the views folder.
For the users nothing has changed, and they can use the contact form as before:
With this refactoring done I can start moving my application to the new structure as explained in the last Python Friday post.
The biggest benefit of Blueprint for me is that you can refactor your application step by step. You do not need to switch to Blueprint and clean-up everything at once. That makes it a great help for existing applications in which you can move to a better structure at your own timetable.
Next
With everything in place to grow my application, it is now time to think about security.
2 thoughts on “Python Friday #42: Using Blueprint to Clean-up Your View Functions File”