Extending Flask with Flask-Script
Last updated on July 27, 2020
Flask Extensions #
Flask Extensions refers to the packages you can install to extend Flask. The idea behind Flask extensions is to provide a consistent and predictable way to integrate packages with Flask. To view, all the available extensions visit http://flask.pocoo.org/extensions/. The page contains packages ranging from sending mail to building full-fledged admin interfaces. Remember that you are not limited to Flask Extensions to extend Flask, In fact, you can use any package from Python standard library or PyPI. The rest of the lesson discusses how to install and integrate a useful Flask extension named Flask-Script.
Flask-Script Extension #
Flask-Script is a nice little extension which allows us to create command line interfaces, run server, start Python shell within the application context, make certain variables available inside the shell automatically and so on.
Recall that in lesson Flask Basics we have disscussed that in order to run the developement server on a specific host and port, we need to pass them as keyword arguments to the run()
method as follows:
1 2 | if __name__ == "__main__":
app.run(debug=True, host="127.0.0.10", port=9000)
|
The problem is that this approach is not very flexible, a better way would be to pass host and port as command line options while starting the server. The Flask-Script extension allows us to do just that. Without further ado, let's install Flask-Script by entering the following command:
(env) overiq@vm:~/flask_app$ pip install flask-script
To use Flask-Script first import Manager
class from flask_script
package and instantiate Manager
object by passing application instance to it. That's how you integrate Flask Extensions, you import the necessary class from the extension package and instantiate it by passing application instance to it. Open main2.py
and modify the file as follows:
flask_app/main2.py
1 2 3 4 5 6 7 | from flask import Flask, render_template
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
...
|
The Manager
object we just created also provides the run()
method but in addition to starting the development server it can also parse command line arguments. Replace the line app.run(debug=True)
with manager.run()
. At this point main2.py
should look like this:
flask_app/main2.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from flask import Flask, render_template
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
@app.route('/')
def index():
return render_template('index.html', name='Jerry')
@app.route('/user/<int:user_id>/')
def user_profile(user_id):
return "Profile page of user #{}".format(user_id)
@app.route('/books/<genre>/')
def books(genre):
return "All Books in {} category".format(genre)
if __name__ == "__main__":
manager.run()
|
Our application now has access to some basic commands. To see the commands available, run main2.py
as follows:
1 2 3 4 5 6 7 8 9 10 | (env) overiq@vm:~/flask_app$ python main2.py
usage: main2.py [-?] {shell,runserver} ...
positional arguments:
{shell,runserver}
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
|
As the output shows, currently we have two commands: shell
and runserver
. Let's start with the runserver
command.
The runserver
command starts the web server. By default, it starts the development server at 127.0.0.1 on port 5000. To see the available options for any command. Type --help
followed by the command name. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | (env) overiq@vm:~/flask_app$ python main2.py runserver --help
usage: main2.py runserver [-?] [-h HOST] [-p PORT] [--threaded]
[--processes PROCESSES] [--passthrough-errors] [-d]
[-D] [-r] [-R] [--ssl-crt SSL_CRT]
[--ssl-key SSL_KEY]
Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
-h HOST, --host HOST
-p PORT, --port PORT
--threaded
--processes PROCESSES
--passthrough-errors
-d, --debug enable the Werkzeug debugger (DO NOT use in production
code)
-D, --no-debug disable the Werkzeug debugger
-r, --reload monitor Python files for changes (not 100% safe for
production use)
-R, --no-reload do not monitor Python files for changes
--ssl-crt SSL_CRT Path to ssl certificate
--ssl-key SSL_KEY Path to ssl key
|
The most commonly used options for runserver
command are --host
and --post
, which allows us to start development server at a specific interface and port. For example:
1 2 | (env) overiq@vm:~/flask_app$ python main2.py runserver --host=127.0.0.2 --port 8000
* Running on http://127.0.0.2:8000/ (Press CTRL+C to quit)
|
The runserver
command by default starts the server without debugger and reloader. We could turn on debugger and reloader manually as follows:
1 2 3 4 5 | (env) overiq@vm:~/flask_app$ python main2.py runserver -d -r
* Restarting with stat
* Debugger is active!
* Debugger PIN: 250-045-653
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
A much simpler way to start the debugger and reloader is to set debug
attribute of the application instance (app
) to True
. Open main2.py
and modify the file as follows:
flask_app/main2.py
1 2 3 4 5 | #...
app = Flask(__name__)
app.debug = True
manager = Manager(app)
#...
|
Let's now explore the shell
command.
The shell
command starts the Python shell inside Flask application context. This simply means that all objects provided by the application and request context are available to you inside the console without creating application or request context. To start shell enter the following command.
1 2 3 | >>>
(env) overiq@vm:~/flask_app$ python main2.py shell
>>>
|
Now let's try accessing some objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>>
>>> from flask import current_app, url_for, request
>>>
>>> current_app.name
'main2'
>>>
>>>
>>> url_for("user_profile", user_id=10)
'/user/10/'
>>>
>>> request.path
'/'
>>>
|
As expected, we can access the desired object without creating application or request context.
Creating Commands #
Once Manager
instance is created, we are ready to create our own commands. There are two ways to create commands:
- Using
Command
class. - Using
@command
decorator.
Creating Commands using Command
class #
Open main2.py
and add the Faker
class as follows:
flask_app/main2.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #...
from flask_script import Manager, Command
#...
manager = Manager(app)
class Faker(Command):
'A command to add fake data to the tables'
def run(self):
# add logic here
print("Fake data entered")
@app.route('/')
#...
|
Here we have created a command Faker
by inheriting the Command
class. The run()
method is called when the command is executed. Now to run this command from the command line, add it to the Manager
instance using the add_command()
method as follows:
flask_app/main2.py
1 2 3 4 5 6 7 8 9 | #...
class Faker(Command):
'A command to add fake data to the tables'
def run(self):
# add logic here
print("Fake data entered")
manager.add_command("faker", Faker())
#...
|
Now head back to terminal and run main2.py
again:
1 2 3 4 5 6 7 8 9 10 11 | (env) overiq@vm:~/flask_app$ python main2.py
usage: main2.py [-?] {faker,shell,runserver} ...
positional arguments:
{faker,shell,runserver}
faker A command to add fake data to the tables
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
|
Note that in addition to shell
and runserver
, we now have an entry for faker
command as well. The description in front of the faker command comes from doc string in the Faker
class. To run it enter the following command:
1 2 | (env) overiq@vm:~/flask_app$ python main2.py faker
Fake data entered
|
Creating commands using @command
decorator #
Creating commands using Command
class is slightly verbose. Alternatively, we can use @command
decorator of the Manager
instance. Open main2.py
and modify the file as follows:
flask_app/main2.py
1 2 3 4 5 6 7 8 9 10 | #...
manager.add_command("faker", Faker())
@manager.command
def foo():
"Just a simple command"
print("foo command executed")
@app.route('/')
#...
|
We have created a simple command foo
which prints foo command executed
when called. The @command
decorator automatically adds the command to the existing Manager
instance, so we don't need to call add_command()
method. Head back to the terminal again and run main2.py
file to see command usage.
1 2 3 4 5 6 7 8 9 10 11 12 | (env) overiq@vm:~/flask_app$ python main2.py
usage: main2.py [-?] {faker,foo,shell,runserver} ...
positional arguments:
{faker,foo,shell,runserver}
faker A command to add fake data to the tables
foo Just a simple command
shell Runs a Python shell inside Flask application context.
runserver Runs the Flask development server i.e. app.run()
optional arguments:
-?, --help show this help message and exit
|
As you can see our foo
command is now available, we can execute it by entering the following command.
1 2 | (env) overiq@vm:~/flask_app$ python main2.py foo
foo command executed
|
Importing Objects Automatically #
Importing lots of objects inside a shell can be tedious. Using Flask-Script, we can make objects available inside the shell without explicitly importing them.
The Shell
command starts a shell. The Shell
constructor function accepts a keyword argument named make_context
. The argument passed to make_context
must be a callable returning a dictionary. By default, the callable returns a dictionary containing just the application instance i.e app
. That means, by default in the shell you only have access to application instance (app
) without explicitly importing it. To override this default, assign a new callable (function) to make_context
which returns a dictionary of objects that you want to access inside the shell.
Open main2.py
and add the following code after the foo()
function.
flask_app/main2.py
1 2 3 4 5 6 7 8 9 10 | #...
from flask_script import Manager, Command, Shell
#...
def shell_context():
import os, sys
return dict(app=app, os=os, sys=sys)
manager.add_command("shell", Shell(make_context=shell_context))
#...
|
Here we are assigning a callable named shell_context
to the make_context
keyword argument. The shell_context()
function returns a dictionary containing three objects: app
, os
and sys
. As a result, inside the shell we will have access to these objects without explicitly importing them.
1 2 3 4 5 6 7 8 9 10 11 12 | (env) overiq@vm:~/flask_app$ python main2.py shell
>>>
>>> app
<Flask 'main2'>
>>>
>>> os.name
'posix'
>>>
>>> sys.platform
'linux'
>>>
>>>
|
Load Comments