|
|
# is52027c lab 20: blueprints
|
|
|
|
|
|
you can use the code provided in lab-20/step-0 as the starter code for this lab.
|
|
|
|
|
|
the exercise is to break out the code using blueprints.
|
|
|
|
|
|
don't forget, you need to replace the value in vs\_url\_for.py with the ID number of your virtual server: mine is 253 so my file contains
|
|
|
|
|
|
URL_PREFIX = '/usr/253'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
in this lab we will learn how flask enables modular applications through blueprints.
|
|
|
|
|
|
there is a good description in the flask documentation: http://flask.pocoo.org/docs/0.12/blueprints/
|
|
|
|
|
|
we'll start from the pre-sqlalchemy code i.e. the version of the app we got to at the end of lab 17. blueprints also work fine with sqlalchemy but we'll implement them like this first.
|
|
|
|
|
|
we will apply blueprints to the mytwits app; this isn't strictly necessary as it is only a small app, but the same methods can be applied to much larger applications.
|
|
|
|
|
|
blueprints are also useful as reusable components.
|
|
|
|
|
|
in this lab we'll
|
|
|
|
|
|
* separate our twit operations into a twits_blueprint (dealing with a database connection issue in the process)
|
|
|
* separate our login operations into a login_blueprint (dealing with an app state issue in the process)
|
|
|
* show how to include templates with the login_blueprint, making it more portable (the template folder structure is a bit non-intuitive)
|
|
|
|
|
|
|
|
|
# step 1: twits\_blueprint
|
|
|
|
|
|
## put the twit views in to a blueprint
|
|
|
|
|
|
* put the routes + views for /, /add\_twit, /edit\_twit and /delete\_twit in to a separate file called twits\_blueprint.py
|
|
|
|
|
|
* import all the modules needed for those views at the start of twits\_blueprint.py
|
|
|
|
|
|
* import the Blueprint module
|
|
|
|
|
|
from flask import Blueprint
|
|
|
|
|
|
* use Blueprint to define the file as a blueprint called 'twits\_blueprint' - see the 'My First Blueprint' example in http://flask.pocoo.org/docs/0.12/blueprints/
|
|
|
|
|
|
twits_blueprint = Blueprint('twits_blueprint', \_\_name\_\_,
|
|
|
templat\e_folder = 'templates')
|
|
|
|
|
|
|
|
|
* change the routes to @twit\_blueprint routes e.g.
|
|
|
|
|
|
@twits_blueprint.route('/add_twit', methods = ['GET', 'POST'])
|
|
|
|
|
|
* register the blueprint in the main mytwits file - see 'Registering Blueprints' in http://flask.pocoo.org/docs/0.12/blueprints/
|
|
|
|
|
|
## database problem
|
|
|
|
|
|
* you will hit a problem here; the database class is instantiated in the main app file i.e.
|
|
|
|
|
|
db = DBHelper()
|
|
|
|
|
|
* but you need it in blueprint; on the other hand if you instantiate it in the blueprint it won't be available in the main file
|
|
|
|
|
|
* the solution is to instantiate the db class in a separate file and import it in to both the blueprint and the main app
|
|
|
|
|
|
* as we already define the DBHelper class in a separate file, we can simply instantiate the app at the end of that,
|
|
|
|
|
|
db = DBHelper()
|
|
|
|
|
|
|
|
|
* then import into twits\_blueprint.py and mytwits\_mysql.py
|
|
|
|
|
|
from dbhelper import db
|
|
|
|
|
|
* check that the app works OK
|
|
|
|
|
|
# step 2: login\_blueprint
|
|
|
|
|
|
* move the login functions to their own file called login\_blueprint.py
|
|
|
|
|
|
* as before make sure you import the modules you will need
|
|
|
|
|
|
* use Blueprint to define the file as a blueprint called 'login\_blueprint' - see the 'My First Blueprint' example in http://flask.pocoo.org/docs/0.12/blueprints/
|
|
|
|
|
|
* change the routes to @login\_blueprint routes
|
|
|
|
|
|
* register the blueprint in the main mytwits file - see 'Registering Blueprints' in http://flask.pocoo.org/docs/0.12/blueprints/
|
|
|
|
|
|
## app state
|
|
|
|
|
|
* the problem we hit here is that we instantiate our login\_manager in logins\_blueprint
|
|
|
|
|
|
login_manager = LoginManager()
|
|
|
|
|
|
* but we need it to be initiated after the main app is instantiated i.e.
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
login_manager.init_app(app)
|
|
|
|
|
|
* the way to get round this is to make use of the record\_once method of the Blueprint Object http://flask.pocoo.org/docs/0.12/api/#blueprint-objects
|
|
|
|
|
|
* this registers a function that will be called the first time a blueprint is registered on the application. the function is called with the state as the argument http://flask.pocoo.org/docs/0.12/api/#flask.blueprints.BlueprintSetupState
|
|
|
|
|
|
* this provides a reference to the current app through flask.blueprints.BlueprintSetupState.app
|
|
|
|
|
|
* the long and short of it is that the following code in the login\_blueprint.py file means that the login manager will be correctly initialised against the app when the blueprint is registered:
|
|
|
|
|
|
@login_blueprint.record_once
|
|
|
def on_load(state):
|
|
|
login_manager.init_app(state.app)
|
|
|
|
|
|
* this means you no longer need to initialise login\_manager in your main app, so delete the line
|
|
|
|
|
|
login_manager.init_app(app)
|
|
|
|
|
|
* check that your application now works OK
|
|
|
|
|
|
* your main app should now consist mainly of the flask app itself, your blueprints and the if \_\__name\_\_ == '\_\_main\_\_' clause
|
|
|
|
|
|
# step-3 (extension)
|
|
|
|
|
|
it's possible to include templates with blueprints, and they will get added to the search path for the app's templates (see http://flask.pocoo.org/docs/0.12/blueprints/#templates).
|
|
|
|
|
|
this means you can parcel up the blueprint in a way that is more self-contained. it can exist in its own folder, with its own templates. this makes it more easy to add to a new project, if you need the same set of functionalities.
|
|
|
|
|
|
however, the way this needs to be done is very specific and could be a bit confusing at first sight. rather than simply having a templates folder inside your blueprints folder, you need to add a subfolder inside that. this is to ensure the blueprint templates don't get overwritten by the app templates (as blueprint templates are essentially 'pulled' in to the main blueprints folder by jinja behind the scenes).
|
|
|
|
|
|
so if we moved the login bloeprint in to its own login folder, and we wanted to include the login.html template with it, we need a templates/login folder _inside_ the login blueprint folder, and we need to refer to the template as login/login.html i.e.
|
|
|
|
|
|
render_template('login/login.html',form=form)
|
|
|
|
|
|
the folder structure is:
|
|
|
|
|
|
/login
|
|
|
login_blueprint.py
|
|
|
__init__.py
|
|
|
/templates
|
|
|
/login
|
|
|
|
|
|
note that i have included an (empty) \_\_init\_\_.py file in the login directory; this is so the login blueprint directory is considered by python to be a package and i can import the login blueprint from it. i will use the following line at the start of my main app to import it:
|
|
|
|
|
|
from login.login_blueprint import login_blueprint
|
|
|
|
|
|
|
|
|
so the recipe for packaging up the login blueprint along with its template is:
|
|
|
|
|
|
* make a login dir
|
|
|
* move login_blueprint.py in to it
|
|
|
* create a blank init file in the directory so python recognises it as a package
|
|
|
|
|
|
touch login/__init__.py
|
|
|
|
|
|
* create a templates directory inside the login directory
|
|
|
* create a login directory inside the templates directory(!)
|
|
|
* move the login.html template in to the login/templates/login directory
|
|
|
* in the login blueprint, change the reference to that template to login/login.html
|
|
|
|
|
|
# step 4: homework
|
|
|
|
|
|
find out how to use templates with the sqlalchemy version of the code. this is actually the more common version shown in online examples, as most people who are at the level of using blueprints will also be using sqlalchemy with flask.
|
|
|
|
|
|
##
|
|
|
|
|
|
that's all folks |