Flask-Sqlalchemy Import/Context Issue

Flask-SQLAlchemy import/context issue

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

Basic Flask SQLAlchemy Context Issue

Perhaps you could try the following:

app.py

from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
import models
from models import initialize_db

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

initialize_db(app)

@app.route('/', methods=['GET', 'POST'])
def home():
return "home"

if __name__ == '__main__':
app.run(debug=True)

models.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

def initialize_db(app):
app.app_context().push()
db.init_app(app)
db.create_all()

class userTable(db.Model):

__tablename__ = "userTable"

id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)

The key is creating a database initialization function within models.py which takes the application instance as a parameter. This function only creates the database tables once it has its application instance. This will allow you to import the models module initially without an application instance and still have a modular design.

Flask, push application context for Flask-sqlalchemy to huey worker

It is better if you let Huey create a Flask app for its use. Organize your code as follows:

Add a second method that creates a Flask app for the specific use by Huey, similar to create_app

#__init__.py
from flask import Flask
from app.config import db, Config, huey
from app.tasks import my_task

def create_app():
# ...
return app

def create_huey_app():
app = Flask('HUEY APP')
app.config.from_object(Config)

# only initialize stuff here that is needed by Huey, eg DB connection

db.init_app(app)

# register any blueprints needed
# e.g. maybe it needs a blueprint to work with urls in email generation

return app

Make all your tasks have a first parameter that is a callable:

#tasks.py
from app.config import huey
from app.models import User

# every task takes an app_factory parameter
@huey.task()
def background_task(app_factory):
app = app_factory()
with app.app_context():
User.query.get(1)
return 1

Then pass create_huey_app as the callable to each task instantiation:

#view.py
from flask import Blueprint
from app.tasks import my_task

main = Blueprint("main", __name__)

@main.route("/")
def index():
# pass the Huey app factory to the task
background_task(create_huey_app) # running the registered background task
return "hello view"

If you want to run the task locally when debugging:

@main.route("/")
def index():
# pass the Huey app factory to the task
if current_app.debug:
background_task.call_local(create_huey_app) # running the task directly
else:
background_task(create_huey_app) # running the task in Huey
return "hello view"

Flask app context for sqlalchemy

You're running a background task in a different thread that doesn't have the application context. You should pass the app object to the background worker. Miguel Grinberg gives an example of this here:

from threading import Thread
from app import app

def send_async_email(app, msg):
with app.app_context():
mail.send(msg)

def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()

Alternatively (and probably the best solution) would be to actually set up a thread-local scoped SQLAlchemy session instead of relying on Flask-SQLAlchemy's request context.

>>> from sqlalchemy.orm import scoped_session
>>> from sqlalchemy.orm import sessionmaker

>>> session_factory = sessionmaker(bind=some_engine)
>>> Session = scoped_session(session_factory)


Related Topics



Leave a reply



Submit