Manage.py and Commands
python manage.py runserver
A note about python manage.py runserver. Runserver is actually located in flask.ext.script. Since we have not specified a runserver command, it defaults to flask.ext.script's Server() method which calls the native flask method app.run(). You can pass in some arguemnts such as changing the port on which the server is run.
The following code block will look for a '.env' file which contains environment variables for things like email address and any other env vars. The .env file will be parsed and santized. Each line contains some "NAME=VALUE" pair. Split this and then store var = "NAME" and var = "VALUE". Then formally set the environment variable in the last line of this block. Per our running example, os.environ["NAME"] = "VALUE" These environment variables can be accessed with "os.getenv('KEY')"
if os.path.exists('.env'): print('Importing environment from .env file') for line in open('.env'): var = line.strip().split('=') if len(var) == 2: os.environ[var] = var
manage.py for more details.
app = create_app(os.getenv('FLASK_CONFIG') or 'default') manager = Manager(app) migrate = Migrate(app, db)
Currently the application will
look for an environment variable called
FLASK_CONFIG or it will
move to the 'default' configuration which is the DevelopmentConfig
manage.py for full details). Next it will call the
create_app method found in
app/__init__.py. This method takes in a
name of a configuration and finds the configuration settings in
config.py. In heroku this will be set to 'production' i.e.
Manager instance is created. Manager
is basically a nice plugin(?) that will allow us to get some useful
feedback when we call
manage.py from the command line. It also handles
manage.py commands. The
decorators are used to determine what the help output should be
on the terminal. Migrate is used to make migration between db instances
really easy. Additionally
@manager.command creates an application
context for use of plugins that are usually tied to the app.
Make Shell Context
def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command('shell', Shell(make_context=make_shell_context)) manager.add_command('db', MigrateCommand)
Make shell context doesn't really serve a ton of purpose in most of our development at h4i. However, it is entirely possible to explore the database from the command line with this as seen in the lines above.
It is possible to create a general app shell or database specific shell. For example doing 'python manage.py shell'
$ me = User() $ db.session.add(me) && db.session.commit() $ me.id >> 1
This basically creates a new user object, commits it to the database gives it a id. The db specific shell exposes the native MigrateCommands... honestly you won't have to worry about these and future info can be found the Flask-Migrate documentation.
@manager.command def recreate_db(): """ Recreates a local database. You probably should not use this on production. EDIT: SHOULD NOT USE THIS IN PRODUCTION!!! """ db.drop_all() db.create_all() db.session.commit()
So this will clear out all the user data (drop_all), will create a new database but with all the tables and columns set up per your models. create_all() and drop_all() rely upon the fact that you have imported ALL YOUR DATABASE MODELS . If you are seeing some table not being created this is the most likely culprit.
Run Worker + Redis
The run_worker command will initialize a task queue. This is basically a list of operations stored in memory that the server will get around to doing eventually. This is great for doing asynchronous tasks. The memory store used for holding these tasks is called Redis. We set up a default redis password and then open a connection to the redis DB. We instantiate a worker and add a queue of items that needs to be processed on that worker.
@manager.command def run_worker(): """Initializes a slim rq task queue.""" listen = ['default'] conn = Redis( host=app.config['RQ_DEFAULT_HOST'], port=app.config['RQ_DEFAULT_PORT'], db=0, password=app.config['RQ_DEFAULT_PASSWORD'] ) with Connection(conn): worker = Worker(map(Queue, listen)) worker.work()
You may/may not know this but the whole if name == 'main' check is to see if this file is being executed directly rather than indirectly (by being imported through another file). So when we execute this file directly (by running python manage.py SOMECMD) we get the option of instatiating the manager instance These methods should be accessible from other files though if imported. << HAVE NOT TESTED THIS THEORY OUT But you would have a tough time executing these commands from cmd line without the Manager init (otherwise you have to deal with argvs and stuff that is frankly tedious).