Factorize application creation logic

This commit is contained in:
Alexis Métaireau 2017-06-29 00:01:03 +02:00
parent 46e3f6eaa2
commit 8229b41bff
3 changed files with 74 additions and 66 deletions

View file

@ -5,7 +5,7 @@ from flask_script import Manager, Command
from flask_migrate import Migrate, MigrateCommand from flask_migrate import Migrate, MigrateCommand
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from ihatemoney.run import app from ihatemoney.run import create_app
from ihatemoney.models import db from ihatemoney.models import db
@ -16,16 +16,15 @@ class GeneratePasswordHash(Command):
password = getpass(prompt='Password: ') password = getpass(prompt='Password: ')
print(generate_password_hash(password)) print(generate_password_hash(password))
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
manager.add_command('generate_password_hash', GeneratePasswordHash)
def main(): def main():
manager.run() app = create_app()
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
manager.add_command('generate_password_hash', GeneratePasswordHash)
manager.run()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -4,20 +4,22 @@ import warnings
from flask import Flask, g, request, session from flask import Flask, g, request, session
from flask_babel import Babel from flask_babel import Babel
from flask_mail import Mail
from flask_migrate import Migrate, upgrade, stamp from flask_migrate import Migrate, upgrade, stamp
from raven.contrib.flask import Sentry from raven.contrib.flask import Sentry
from ihatemoney.web import main, db, mail
from ihatemoney.api import api from ihatemoney.api import api
from ihatemoney.models import db
from ihatemoney.utils import PrefixedWSGI from ihatemoney.utils import PrefixedWSGI
from ihatemoney.utils import minimal_round from ihatemoney.web import main as web_interface
from ihatemoney import default_settings from ihatemoney import default_settings
app = Flask(__name__, instance_path='/etc/ihatemoney', instance_relative_config=True)
def setup_database(app):
"""Prepare the database. Create tables, run migrations etc."""
def pre_alembic_db(): def _pre_alembic_db():
""" Checks if we are migrating from a pre-alembic ihatemoney """ Checks if we are migrating from a pre-alembic ihatemoney
""" """
con = db.engine.connect() con = db.engine.connect()
@ -25,8 +27,23 @@ def pre_alembic_db():
alembic_setup = db.engine.dialect.has_table(con, 'alembic_version') alembic_setup = db.engine.dialect.has_table(con, 'alembic_version')
return tables_exist and not alembic_setup return tables_exist and not alembic_setup
db.init_app(app)
db.app = app
def configure(): Migrate(app, db)
migrations_path = os.path.join(app.root_path, 'migrations')
if _pre_alembic_db():
with app.app_context():
# fake the first migration
stamp(migrations_path, revision='b9a10d5d63ce')
# auto-execute migrations on runtime
with app.app_context():
upgrade(migrations_path)
def load_configuration(app):
""" A way to (re)configure the app, specially reset the settings """ A way to (re)configure the app, specially reset the settings
""" """
default_config_file = os.path.join(app.root_path, 'default_settings.py') default_config_file = os.path.join(app.root_path, 'default_settings.py')
@ -43,6 +60,9 @@ def configure():
app.config.from_pyfile('ihatemoney.cfg', silent=True) app.config.from_pyfile('ihatemoney.cfg', silent=True)
app.wsgi_app = PrefixedWSGI(app) app.wsgi_app = PrefixedWSGI(app)
def validate_configuration(app):
if app.config['SECRET_KEY'] == default_settings.SECRET_KEY: if app.config['SECRET_KEY'] == default_settings.SECRET_KEY:
warnings.warn( warnings.warn(
"Running a server without changing the SECRET_KEY can lead to" "Running a server without changing the SECRET_KEY can lead to"
@ -57,64 +77,55 @@ def configure():
+ " and will be removed in further version", + " and will be removed in further version",
UserWarning UserWarning
) )
if not 'MAIL_DEFAULT_SENDER' in app.config: if 'MAIL_DEFAULT_SENDER' not in app.config:
app.config['MAIL_DEFAULT_SENDER'] = DEFAULT_MAIL_SENDER app.config['MAIL_DEFAULT_SENDER'] = default_settings.DEFAULT_MAIL_SENDER
if "pbkdf2:sha256:" not in app.config['ADMIN_PASSWORD'] and app.config['ADMIN_PASSWORD']: if "pbkdf2:sha256:" not in app.config['ADMIN_PASSWORD'] and app.config['ADMIN_PASSWORD']:
# Since 2.0 # Since 2.0
warnings.warn( warnings.warn(
"The way Ihatemoney stores your ADMIN_PASSWORD has changed. You are using an unhashed" "The way Ihatemoney stores your ADMIN_PASSWORD has changed. You are using an unhashed"
+" ADMIN_PASSWORD, which is not supported anymore and won't let you access your admin" + " ADMIN_PASSWORD, which is not supported anymore and won't let you access your admin"
+" endpoints. Please use the command './budget/manage.py generate_password_hash'" + " endpoints. Please use the command './budget/manage.py generate_password_hash'"
+" to generate a proper password HASH and copy the output to the value of" + " to generate a proper password HASH and copy the output to the value of"
+" ADMIN_PASSWORD in your settings file.", + " ADMIN_PASSWORD in your settings file.",
UserWarning UserWarning
) )
configure()
def create_app(instance_path='/etc/ihatemoney'):
app = Flask(__name__, instance_path=instance_path,
instance_relative_config=True)
load_configuration(app)
validate_configuration(app)
app.register_blueprint(web_interface)
app.register_blueprint(api)
app.register_blueprint(main) # Configure the application
app.register_blueprint(api) setup_database(app)
# custom jinja2 filters mail = Mail()
app.jinja_env.filters['minimal_round'] = minimal_round mail.init_app(app)
app.mail = mail
# db # Error reporting
db.init_app(app) Sentry(app)
db.app = app
# db migrations # Translations
migrate = Migrate(app, db) babel = Babel(app)
migrations_path = os.path.join(app.root_path, 'migrations')
if pre_alembic_db(): @babel.localeselector
with app.app_context(): def get_locale():
# fake the first migration
stamp(migrations_path, revision='b9a10d5d63ce')
# auto-execute migrations on runtime
with app.app_context():
upgrade(migrations_path)
# mail
mail.init_app(app)
# translations
babel = Babel(app)
# sentry
sentry = Sentry(app)
@babel.localeselector
def get_locale():
# get the lang from the session if defined, fallback on the browser "accept # get the lang from the session if defined, fallback on the browser "accept
# languages" header. # languages" header.
lang = session.get('lang', request.accept_languages.best_match(['fr', 'en'])) lang = session.get('lang', request.accept_languages.best_match(['fr', 'en']))
setattr(g, 'lang', lang) setattr(g, 'lang', lang)
return lang return lang
return app
def main(): def main():
app = create_app()
app.run(host="0.0.0.0", debug=True) app.run(host="0.0.0.0", debug=True)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -13,10 +13,9 @@ from flask import (
Blueprint, current_app, flash, g, redirect, render_template, request, Blueprint, current_app, flash, g, redirect, render_template, request,
session, url_for, send_file session, url_for, send_file
) )
from flask_mail import Mail, Message from flask_mail import Message
from flask_babel import get_locale, gettext as _ from flask_babel import get_locale, gettext as _
from werkzeug.security import generate_password_hash, \ from werkzeug.security import check_password_hash
check_password_hash
from smtplib import SMTPRecipientsRefused from smtplib import SMTPRecipientsRefused
import werkzeug import werkzeug
from sqlalchemy import orm from sqlalchemy import orm
@ -31,7 +30,6 @@ from ihatemoney.forms import (
from ihatemoney.utils import Redirect303, list_of_dicts2json, list_of_dicts2csv from ihatemoney.utils import Redirect303, list_of_dicts2json, list_of_dicts2csv
main = Blueprint("main", __name__) main = Blueprint("main", __name__)
mail = Mail()
def requires_admin(f): def requires_admin(f):
@ -337,7 +335,7 @@ def invite():
body=message_body, body=message_body,
recipients=[email.strip() recipients=[email.strip()
for email in form.emails.data.split(",")]) for email in form.emails.data.split(",")])
mail.send(msg) current_app.mail.send(msg)
flash(_("Your invitations have been sent")) flash(_("Your invitations have been sent"))
return redirect(url_for(".list_bills")) return redirect(url_for(".list_bills"))