Initialize a Flask-WhooshAlchemy index when using the app factory pattern - python

I am developing an app with Flask using the application factory pattern. Initializing the Whoosh index doesn't work, because current_app cannot be used without setting up an app context explicitly. How do I do this?
__init__.py
from flask import Flask
from .frontend import frontend
def create_app(configfile=None):
app = Flask(__name__)
from .models import db
db.init_app(app)
app.register_blueprint(frontend)
return app
models.py
from flask_sqlalchemy import SQLAlchemy
import flask_whooshalchemy as wa
from flask import current_app
db = SQLAlchemy()
class Institution(db.Model):
__searchable__ = ['name', 'description']
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40))
description = db.Column(db.Text)
# this does not work
wa.whoosh_index(current_app, Institution)

You can't use current_app outside an application context. An application context exists during a request, or when explicitly created with app.app_context().
Import your models during create_app and index them with Whoosh there. Remember, the factory is where you're performing all setup for the application.
def create_app():
app = Flask('myapp')
...
from myapp.models import Institution
wa.whoosh_index(app, Institution)
...
return app
If you want to keep the code local to the blueprint, you can use the blueprint's record_once function to perform the index when the blueprint is registered on the app.
#bp.record_once
def record_once(state):
wa.whoosh_index(state.app, Institution)
This will be called at most once when registering the blueprint with an app. state contains the app, so you don't need the current_app.

Related

How to separate flask files when two files depend on each other?

I'm trying to develop a database driven flask app.
I have an api.py file which has the flask app, api and SQLAlchemy db objects and a users.py file which contains the routes ands code to create a database table.
In the users.py file, there's a UserManager Resource which has the routes. I have to add this resource to the API from this file.
So users.py needs to import the db and the api from the api.py file and api.py needs to serve the flask app so it has all the variables users.py needs, but api.py also needs to import users.py in order to use it as a flask blueprint.
api.py:
...
from user import user
app = Flask(__name__)
app.register_blueprint(user)
api = Api(app)
...
db = SQLAlchemy(app)
ma = Marshmallow(app)
...
if __name__ == '__main__':
app.run(debug=True)
users.py:
from api import api
user = Blueprint('user', __name__, template_folder='templates')
db = SQLAlchemy(api.app)
ma = Marshmallow(api.app)
class User(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
username = db.Column(db.String(32), unique=True, nullable=False)
password = db.Column(db.String(32))
first_name = db.Column(db.String(32))
last_name = db.Column(db.String(32))
...
...
class UserManager(Resource):
#user.route('/get/<user_id>', methods = ['GET'])
def get_user(user_id):
...
This of course results in errors due to circular imports. Question is, how do I separate the flask files with the routes from the api file when the blueprint has dependencies from the api (the db and the api objects)
I also somehow have to do something like api.add_resource(UserManager, '/api/users') but I'm not sure where that'd go given the circular import.
Tried reducing dependencies between two files, but couldn't achieve described goal without doing a 2 way import.
Either trying to get 2 way import to work or still have same structure of separate files with routes but using 1 way import.
This is a well known problem in flask. The solution is to use application factories.
Cookiecutter Flask does this really well and offers a good template. It is well worth to check out their repo and try to understand what they are doing.
Assuming you have a folder app and this folder contains a file __init__.py and your other files user.py, etc.
Create a file app/extensions.py with this content and any other extension you need to initialize.
...
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
Import the db object (which was created, but not initialized) in the files where you need it.
from app.extensions import db
In your app/api.py file
from flask import Flask
from app.extensions import db
from app.user import user as user_bp
def create_app():
app = Flask(__name__)
register_extensions(app)
register_blueprints(app)
return app
def register_extensions(app):
"""Register Flask extensions."""
db.init_app(app)
return None
def register_blueprints(app):
"""Register Flask blueprints."""
app.register_blueprint(user_bp)
return None
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
Add stuff based on this approach as needed.

Access Flask-SQLAlchemy database outside of view functions

I have created a small Flask application which stores its data in an sqlite database that I access via flask-sqlalchemy.
However, when I run it, I get the following error:
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.
I have debugged my application and now know that this error stems from these two functions:
def user_exists(email):
if User.query.filter_by(email = email).count() == 0:
return False
else:
return True
def get_user(email):
user = User.query.filter_by(email = email).first()
return user
Now I am wondering: Is it impossible to access the database via flask-sqlalchemy outside of view functions?
For further context, I added the files in which I configure my flask app:
presentio.py
from app import create_app
app = create_app(os.getenv("FLASK_CONFIG", "default"))
app/init.py
from flask_mail import Mail
from flask_sqlalchemy import SQLAlchemy
from config import config
mail = Mail()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
mail.init_app(app)
db.init_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix = "/auth")
from .text import text as text_blueprint
app.register_blueprint(text_blueprint, url_prefix = "/text")
return app
You need to give the flask app a context after you create it.
This is done automatically in view functions, but outside those, you need to do this after you create the app:
app.app_context().push()
See the docs: https://flask-sqlalchemy.palletsprojects.com/en/2.x/contexts/

Integrate graphql into flask blueprint

I'm currently trying to integrate GraphQL (using graphene and flask_graphql) into my flask app. Tried a few tutorial from here and here. But seems none works in my situation.
Currently my project is like this
-manage.py
|app
|__init__.py (create_app is here)
|mod_graphql (this is folder)
|__init__.py (blueprint created here)
|models.py
|schema.py
|controller.py
The issue is in both tutorial, it recommend to create Base and engine in the models file, I did the same, but I would need to read config file using current_app for the URI to create engine. But actually, because it happens in flask initialization, so there is no request context yet, so current_app doesn't exist. so everything fails.
Would it be possible to help me setup this?
Below are some code:
app/__init__.py
create_app():
...
from .mod_graphql import bp_graph
app.register_blueprint(bp_graph)
...
mod_graphql/__init__.py
from flask import Blueprint
bp_graph = Blueprint('graphql', __name__)
from . import controller
from . import models
from . import schema
mod_graphql/models.py
Base = declarative_base()
engine = create_engine(current_app.config.get('BASE_URI'),
convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base.query = db_session.query_property()
class Model1_Model(Base):
...
class Model2_Model(Base):
...
mod_graphql/schema.py
class Model1(SQLAlchemyObjectType):
class Meta:
model = Model1_Model
interfaces = (relay.Node,)
class Model2(SQLAlchemyObjectType):
class Meta:
model = Model2_Model
interfaces = (relay.Node,)
class Query(graphene.ObjectType):
node = relay.Node.Field()
schema = graphene.Schema(query=Query, types=[Model1])
mod_graphql/controller.py
bp_graph.add_url_rule('/graphql',
view_func=GraphQLView.as_view('graphql',
schema=schema,
graphiql=True,
context={'session':
db_session}))
#bp_graph.teardown_app_request()
def shutdown_session(exception=True):
db_session.remove()
When I try to start server, it tells me:
Working outside of application context
Would you pls recommend the best practice to setup this?
Thanks a lot!
I would suggest using the flask initialization to define the DB connection. This way you can use Flask's internal implementation of connecting to the database without defining it on your own
app/__init__.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
Then all you need to do in your models is:
app/models.py
Base = declarative_base()
class Model1_Model(Base):
Then in your route definition instead of using the db_seesion from your model inside your context, you can reference Flask's db like context={'session': db.session}) that you created in the create_app() function.
Try initiate the Blueprint in
app.__init__
I got a same time like u,cos the
__name__
should be app
Finally figured out how to do it without using current_app:
in config.py, and multi database binding by:
SQLALCHEMY_BINDS = {
'db1': DB1_URI,
'db2': DB2_URI
}
in models.py, do this
engine = db.get_engine(bind='db1')
metaData = MetaData()
metaData.reflect(engine)
Base = automap_base(metadata=MetaData)
class Model1(Base):
__tablename__ = 'db1_tablename'
In this way, the model will be able to access the proper database uri without current_app, but if app_context switches, potentially different solution will be needed

access app config variables in Flask app

I have a flask app that looks like this:
myapp
-- application.py
-- models.py
-- queries.py
-- routes.py
-- settings.py
application.py looks like this:
from flask import Flask
from myapp import routes
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(routes.main)
return app
queries.py looks like this
from myapp.models import User
class QueryConnection(DATABASE_URI):
self.engine = create_engine(DATABASE_URI)
self.session = sessionmaker(bind=self.engine)
self.Session = self.session()
def get_all_users(self):
return self.Session.query(User).all()
routes.py looks like this:
from flask import current_app, Blueprint, render_template
from myapp import queries
main = Blueprint('main', __name__, url_prefix='/')
query_connection = queries.QueryConnection(current_app.config['DATABASE_URI'])
#main.route("/")
def index():
return render_template('index.jinja2', list=query_connection.get_all_users())
models.py looks like this:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
I want to get the current_app.config['DATABASE_URI'] in routes.py so I can query the database but I don't know how to pass the app context from application.py to queries.py. I tried using this in application.py
with app.app_context():
app.register_blueprint(main)
But I get the error "working outside of application context". Anything else gives the same error.
I want to encapsulate the queries in its own class so that I can pass the database uri in different contexts like in tests.
you need to import the file, from where you are calling the create_app method. My guess is that it is the application.py.
--- D_file.py
app = create_app(your_config)
--- queries.py
from D_file import app
DB_URI = app.config['DATABASE_URI']
Why are you involving the Flask application at all in the SQLAlchemy setup? You have a configuration object available to you, use that directly. Then you can attach a session to the flask global object, or create a session dynamically.
In queries.py
import get_config from config
config_object = get_config()
engine = create_engine(config_object['DATABASE_URI'])
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
def get_all_users():
session = Session()
users = session.query(User).all()
session.close()
return users
And in your app factory:
from flask import Flask, g
from myapp import routes
from myapp.queries import Session
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
app.register_blueprint(routes.main)
# automatically set up and close a session for each request
#app.before_request
def before_request(response):
g.session = Session()
#app.after_request
def after_request(response):
g.session.close()
return app

Flask-SQLAlchemy import/context issue

I want to structure my Flask app something like:
./site.py
./apps/members/__init__.py
./apps/members/models.py
apps.members is a Flask Blueprint.
Now, in order to create the model classes I need to have a hold of the app, something like:
# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy
db = SQLAlchemy(current_app)
class Member(db.Model):
# fields here
pass
But if I try and import that model into my Blueprint app, I get the dreaded RuntimeError: working outside of request context. How can I get a hold of my app correctly here? Relative imports might work but they're pretty ugly and have their own context issues, e.g:
from ...site import app
# ValueError: Attempted relative import beyond toplevel package
The flask_sqlalchemy module does not have to be initialized with the app right away - you can do this instead:
# apps.members.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
# fields here
pass
And then in your application setup you can call init_app:
# apps.application.py
from flask import Flask
from apps.members.models import db
app = Flask(__name__)
# later on
db.init_app(app)
This way you can avoid cyclical imports.
This pattern does not necessitate the you place all of your models in one file. Simply import the db variable into each of your model modules.
Example
# apps.shared.models
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# apps.members.models
from apps.shared.models import db
class Member(db.Model):
# TODO: Implement this.
pass
# apps.reporting.members
from flask import render_template
from apps.members.models import Member
def report_on_members():
# TODO: Actually use arguments
members = Member.filter(1==1).all()
return render_template("report.html", members=members)
# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members
reporting = Blueprint("reporting", __name__)
reporting.route("/member-report", methods=["GET","POST"])(report_on_members)
# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting
app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)
Note: this is a sketch of some of the power this gives you - there is obviously quite a bit more that you can do to make development even easier (using a create_app pattern, auto-registering blueprints in certain folders, etc.)
an original app.py: https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/
...
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
class Computer(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
# Create the database tables.
db.create_all()
...
# start the flask loop
app.run()
I just splitted one app.py to app.py and model.py without using Blueprint. In that case, the above answer dosen't work. A line code is needed to work.
before:
db.init_app(app)
after:
db.app = app
db.init_app(app)
And, the following link is very useful.
http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

Categories

Resources