Extending Flask with Flask-Script

by Alex
Extending Flask with Flask-Script

Flask extensions

Flask extensions are packages that you can install to extend the capabilities of Flask. Their point is to provide a convenient and clear way to integrate packages into Flask. To see all the available extensions, visit https://flask.pocoo.org/extenstions/. The page has packages whose capabilities range from sending emails to creating full-fledged administrator interfaces. It’s important to keep in mind that you can extend Flask’s capabilities with more than just its extensions. In fact, any package from the standard Python or PyPi library will do. The rest of the tutorial is about how to install and integrate a handy extension for Flask called Flask-Script.

Flask-Script extension

Flask-Script is a handy miniature extension that allows you to create command line interfaces, run the Python server and console in the context of applications, make certain variables visible in the console automatically, and so on. It’s worth recalling what was discussed in the Flask Basics lesson. To run a development server on a particular host and port, you have to pass them as arguments-keywords to the run() method:

if __name__ == "__main__":
    app.run(debug=True, host="127.0.0.10", port=9000)

The problem is that this approach is not flexible. It is much more convenient to pass the host and port as command line parameters when starting the server. Flask-Script allows you to do this. You can install Flask-Script using pip:

(env) [email protected]:~/flask_app$ pip install flask-script

To use Flask-Script, you must first import the Manager class from the flask_script package and create an instance of the Manager object by passing an application instance to it. In this way the Flask extensions are integrated. First you import the desired class from the package, and then create an instance by passing the application instance to it. You need to open the file main2.py and modify it as follows:

from flask import Flask, render_template
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

#...

The created Manager object also has a run() method, which in addition to running the development server can read command line arguments. You should replace the line app.run(debug=True) to manager.run(). At this point main2.py should look like this:

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()

The application now has access to basic commands. To see which ones are available, run main2.py:

(env) [email protected]:~/flask_app$ python main2.py
usage: main2.py [-?] {shell,runserver} ...

positional arguments:
  {shell,runserver}
    shell Runs a Python shell inside a 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, there are now only two commands: shell and runserver. Let’s start with the runserver command. runserver runs the web server. By default, it runs on 127.0.0.1 on port 5000. To see the options for any command, you need to type --help and the command itself. For example:

(env) [email protected]:~/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 widely used options for runserver are --host and --post. You can use them to run a development server on a specific interface and port. For example:

(env) [email protected]:~/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)

By default, the runserver command runs the server without a debugger. You can enable it manually as follows:

(env) [email protected]:~/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 simpler way to start the debugger is to select True for the debug attribute of the object(app) instance. To do this, open main2.py and modify the file as follows:

#...
app = Flask(__name__)
app.debug = True
manager = Manager(app)
#...

Next, about the shell command. shell runs the Python console in the context of the Flask application. This means that all objects within the application and request contexts will be available in the console without creating additional contexts. To start the console you need to enter the following command.

(env) [email protected]:~/flask_app$ python main2.py shell

Get access to certain objects.

>>>
>>> > 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, this can be done without creating application and query contexts.

Creating Commands

Once the Manager instance has been created, you can start creating your own commands. There are two ways to do this:

  1. With the Commandclass
  2. With @commanddecorator

Creating commands with the Command

class In the main2.py file, add the Faker class:

#...
from flask_script import Manager, Command
#...

manager = Manager(app)

class Faker(Command):
   'Command to add fake data to tables'
  def run(self):
       # function logic
      print("Fake data entered")
       
@app.route('/')
#...

The Faker command was created by inheriting the Command class. The run() method is called when the command is executed. To execute a command through the command line, you need to add it to a Manager instance using the add_command() method:

#...
class Faker(Command):
   'Command to add fake data to tables'
  def run(self):
       # function logic
      print("Fake data entered")
       
manager.add_command('faker', Faker())
#...

Now you need to go back to the terminal again and run the file main2.py:

(env) [email protected]:~/flask_app$ python main2.py
usage: main2.py [-?] {faker,shell,runserver} ...

positional arguments:
  {faker,shell,runserver}
    faker Command to add fake data to 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 there is now a faker command in addition to shell and runserver. The description before the command itself is taken from the documentation line of the Faker class. To run it, enter the following command:

(env) [email protected]:~/flask_app$ python main2.py faker
Fake data entered

Creating commands with the @command

decorator Creating commands using the Command class is quite extensive. Alternatively, you can use the @command decorator of an instance of the Manager class. To do this, open file main2.py and change it as follows:

#...
manager.add_command("faker", Faker())

@manager.command
def foo():
   "This is the command that was created."
  print("foo command executed")

@app.route('/')
#...

A simple foo command was created that outputs foo command executed when called. The @command decorator automatically adds the command to an existing Manager instance, so you don’t need to call the add_command() method. To see how the commands are used, you need to go back to the terminal and run main2.py.

(env) [email protected]:~/flask_app$ python main2.py
usage: main2.py [-?] {faker,foo,shell,runserver} ...

positional arguments:
  {faker,foo,shell,runserver}
    faker Command to add fake data to tables
    foo This is a created command
    shell Runs a Python shell inside a Flask application context.
    runserver Runs the Flask development server i.e. app.run()

optional arguments:
-?, --help show this help message and exit

Since the foo command is now available, you can execute it by entering the following command.

(env) [email protected]:~/flask_app$ python main2.py foo
foo command executed

Automatic import of objects

Importing a large number of objects on the command line can be tedious. With Flask-Script you can make objects visible in the terminal without explicitly importing them. The Shell command launches a shell. The Shell constructor function takes the argument-keyword make_context. The argument passed to make_context must be callable and return a dictionary. By default, the callable object returns a dictionary containing only an instance of the application, i.e., app. This means that by default the shell can only access the application instance(app) without specifically importing it. To change this behavior, you need to assign make_context to a new call-supporting object (function). This will return a dictionary with the objects you want to access inside the shell. Let’s open the main2.py file to add the following code after the foo() function.

#...
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, the called shell_context() function is passed the make_context keyword argument. The shell_context function returns a dictionary with three objects: app, os, and sys. As a result, these objects are now available inside the shell, even though they were not imported.

(env) [email protected]:~/flask_app$python main2.py shell
>>>
>>> app
<Flask 'main2'>
>>>
>>> os.name
'posix'
>>>
>>> sys.platform
'linux'
>>>
>>>

Related Posts

LEAVE A COMMENT