using flask-migrate with flask-script and application factory - flask

I'm building flask application and decided to try application factory approach this time, but got into trouble with flask-migrate and can't figure out simple solution.
Please note that I want to pass config location as an option to the script
manage.py:
manager = Manager(create_app)
manager.add_option("-c", "--config", dest="config_module", required=False)
then i need to create migrate instance and add command to the manager:
with manager.app.app_context():
migrate = Migrate(current_app, db)
manager.add_command('db', MigrateCommand)
but app instance is not created yet, so it fails
I know I can pass config in environment variable and create application before creating manager instance, but how to do it using manager options?

When you use a --config option you have to use an application factory function as you know, as this function gets the config as an argument and that allows you to create the app with the right configuration.
Since you have an app factory, you have to use the deferred initialization for all your extensions. You instantiate your extensions without passing any arguments, and then in your app factory function after you create the application, you call init_app on all your extensions, passing the app and db now that you have them.
The MigrateCommand is completely separate from this, you can add that to the Manager without having app and db instances created.
Example:
manage.py:
from app import create_app
from flask_migrate import MigrateCommand, Manager
manager = Manager(create_app)
manager.add_option("-c", "--config", dest="config_module", required=False)
manager.add_command('db', MigrateCommand)
app.py (or however your app factory module is called)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
def create_app(config):
app = Flask(__name__)
# apply configuration
# ...
# initialize extensions
db.init_app(app)
migrate.init_app(app, db)
return app

Related

How to get a handle to the initialized database from a Flask application with SQLAlchemy?

I would like to add data to a database with Flask-SQLAlchemy without the Flask app running.
Is there a way to get db back from the app after the app and the database have been initialized.
My code looks like
db = SQLAlchemy()
def init_app():
app = Flask(__name__)
db = SQLAlchemy(app)
db.init_app(app)
return app
And what I would like to do is something like
from app import init_app
app = init_app() # initialized but not running
# db is used in model.py, but not initialized
# with Flask
# from db = SQLAlchemy()
from model import Machine # class Machine(db.Model)
p = Machine(name='something')
# now I need the initialized db from somewhere
db.session.add(p)
db.session.commit()
Basically I would like to do what's described here:
Another disadvantage is that Flask-SQLAlchemy makes using the database
outside of a Flask context difficult. This is because, with
FLask-SQLAlchemy, the database connection, models, and app are all
located within the app.py file. Having models within the app file, we
have limited ability to interact with the database outside of the app.
This makes loading data outside of your app difficult. Additionally,
this makes it hard to retrieve data outside of the Flask context.
Well, once you initialize the app, Flask spines a server (either development or production, whichever you set), so if you would like to add data to a database with Flask-SQLAlchemy without the Flask app running, you would better use the flask shell command which runs in the context of the current app, then you could add your data.
But first, it would be better is you set up your app as the following so we could directly import stuff like db, auth, etc:
...
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def init_app():
app = Flask(__name__)
db.init_app(app)
return app
In the root of your project, type in the terminal the following command
flask shell
Now that you have a shell running in the context of the current app but not the server not running:
from app import db
from model import Machine # class Machine(db.Model)
p = Machine(name='something')
# now I need the initialized db from somewhere
db.session.add(p)
db.session.commit()
From the wonderful tutorial...
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database
Define something like this...
from app import app, db
from app.models import User, Post
#app.shell_context_processor
def make_shell_context():
return {'db': db, 'User': User, 'Post': Post}
And then...
(venv) $ flask shell
>>> db
<SQLAlchemy engine=sqlite:////Users/migu7781/Documents/dev/flask/microblog2/app.db>
>>> User
<class 'app.models.User'>
>>> Post
<class 'app.models.Post'>

How can I initialize the database automatically with SQLalchemy and Alembic?

Currently, I run
$ flask db init
$ flask db migrate -m "initialization"
$ flask db upgrade
if the database does not exist. I would like to run this within Python, e.g. something like
app.create_db()
so that I don't have to care about setting the database up. Is that possible?
I use the flask-sqlalchemy and flask-migrations plugins
You can use SQLAlchemy-Utils for this.
from sqlalchemy import create_engine
from sqlalchemy_utils import database_exits,create_database
def validate_database():
engine = create_engine('postgres://postgres#localhost/name')
if not database_exists(engine.url): # Checks for the first time
create_database(engine.url) # Create new DB
print("New Database Created"+database_exists(engine.url)) # Verifies if database is there or not.
else:
print("Database Already Exists")
call this method in your __init__.py file so that it checks every time your server starts.
Obviously, you have installed flask-migrate, flask-sqlalchemy.
So, you can do like this:
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
db.create_all()
API DOC: flask.ext.sqlalchemy.SQLAlchemy.create_all
but your Question has confused me. why restricted by SQLAlchemy and Alembic?
There's db.create_all() but I think that when you're using migrations you should stick to migration scripts.
Something to note is that if you have your migration files all set up (i.e migration folder) then all you need is flask db migrate
If you're running this locally, I would stick to doing this command manually.
If you're using this on a server, you should probably use a deployment script that does this for you. You can look at fabric (www.fabfile.org) for information on how to run terminal commands

Flask: what happened to the script_info_option?

I wanted it to add common app-configuration options such as --config, --loglevel, --logfile, etc. to my flask.cli group and read them from the scriptinfo in my app factory function.
The #script_info_option decorator was apparently removed after 0.11 with a cryptic commit message like "implementing simplified interface".
So... how do I do add app-factory-time configuration options now?
Found out how you do it now:
Decorate your app factory with #click.pass_context so it gets the context as the first argument.
In the app factory, use ctx.find_root().params to get what was passed to the group.
You can create an app factory and pass arguments to it with the #pass_script_info decorator like so...
manage.py
#!/usr/bin/env python
import click
import config
from flask import Flask
from flask.cli import FlaskGroup, pass_script_info
def create_app(script_info):
app = Flask(__name__)
if script_info.config_mode:
obj = getattr(config, kwargs["config_mode"])
flask_config.from_object(obj)
...
return app
#click.group(cls=FlaskGroup, create_app=create_app)
#click.option('-m', '--config-mode', default="Development")
#pass_script_info
def manager(script_info, config_mode):
script_info.config_mode = config_mode
if __name__ == "__main__":
manager()
config.py
class Config(object):
TESTING = False
class Production(Config):
DATABASE_URI = 'mysql://user#localhost/foo'
class Development(Config):
DATABASE_URI = 'sqlite:///app.db'
class Testing(Config):
TESTING = True
DATABASE_URI = 'sqlite:///:memory:'
Now in the command line (after running pip install manage.py) you can do manage -m Production run.

flask-security: how to use in blueprint/extension app pattern?

I want to use flask-security.
I'm using a template flask app which creates global objects for extensions, and then initialises them when the app is created.
e.g. in extensions.py there is code like this:
from flask_bcrypt import Bcrypt
from flask_caching import Cache ...
from flask_security import Security ...
bcrypt = Bcrypt() ...
security = Security()
and then in app.py a call to register_extensions(app) which uses init_app(app) methods like so:
bcrypt.init_app(app)
security.init_app(app)
and indeed flask-security has an init_app() method. But the documentation says that the Security object needs a DataStore object which needs the User and Role model. It doesn't feel right to import the User and Role model in app.py when so far no other extension needs that.
What is the best practice for using Flask-Security when using the 'large Flask app' model ... I don't find the documentation helpful. It is a simple case when all objects are defined in one place.
Below is what I have.
extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security
db = SQLAlchemy()
security = Security()
__init__.py
from .extensions import db, security
from .models import User, Role
def create_app(config_name):
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security.init_app(app, user_datastore)

Importing Flask app when using app factory and flask script

This is Flask app context
app = Flask(__name__)
with app.app_context():
# insert code here
Most of the use cases of app context involves having 'app' initialized on the same script or importing app from the base.
My application is structured as the following:
# application/__init__.py
def create_app(config):
app = Flask(__name__)
return app
# manage.py
from application import create_app
from flask_script import Manager
manager = Manager(create_app)
manager.add_command("debug", Server(host='0.0.0.0', port=7777))
This might be really trivial issue, but how I should call 'with app.app_context()' if my application is structured like this?
Flask-Script calls everything inside the test context, so you can use current_app and other idioms:
The Manager runs the command inside a Flask test context. This means that you can access request-local proxies where appropriate, such as current_app, which may be used by extensions.
http://flask-script.readthedocs.org/en/latest/#accessing-local-proxies
So you don't need to use with app.app_context() with Manager scripts. If you're trying to do something else, then you'd have to create the app first:
from application import create_app
app = create_app()
with app.app_context():
# stuff here