151

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
Brad Wright
  • 5,602
  • 6
  • 29
  • 30

2 Answers2

392

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

Jeff Widman
  • 22,014
  • 12
  • 72
  • 88
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • 2
    Can you do that multiple times? For example if I have more than one models.py file? – Brad Wright Mar 14 '12 at 06:50
  • @BradWright - it makes it easier if you create only `db` instance for each database that you have. If you have a package of models you can put it in `__init__.py`. However you choose to do it, you simply import the `db` variable from that location into your other model files and use it as normal. When they are loaded everything resolves correctly. – Sean Vieira Mar 15 '12 at 02:28
  • 1
    Would you happen to have a link to a project that is set up this way? – Mbrevda May 29 '13 at 15:07
  • 4
    @Mbrevda - you can see an example of this pattern here https://github.com/svieira/Budget-Manager – Sean Vieira May 29 '13 at 15:30
  • is the `.ext` namespace still necessary, or should we just import from `flask_sqlalchemy` these days? – Rob Grant Aug 30 '15 at 13:38
  • 1
    The `.ext.` namespace is deprecated - it's better to import from the real namespace (`flask_sqlalchemy`). – Sean Vieira Aug 30 '15 at 23:14
  • This is a great answer and should be included in the official flask-sqlalchemy documentation as I was looking for this for quite a while. – Flipper Jan 14 '19 at 20:56
  • I don't know what it's lacking, but this whole example creates the database, but the table for the model... – ivanleoncz Dec 11 '19 at 22:29
  • Can we also do CustomBase with this one? – technazi Jan 17 '20 at 21:18
  • what if a model need to be imported in another model file? – Talha Junaid Mar 13 '21 at 21:14
  • There's no harm to that. `DB` gets imported into `ModelA` and `DB + ModelA` get imported into `ModelB` - there's no circular import issues. – Sean Vieira Mar 16 '21 at 22:12
  • Hi, you have a comment on top of your files eg. # apps.application . How does it help? This is new to me and i would love to learn if it does something extra. – Max08 Aug 28 '21 at 20:33
  • Hi @Max08 - the comment is just to show you the name of the module the code snippet represents. This makes it easier for people to follow along here on StackOverflow, it doesn't have any meaning to Python and you don't need it in your real application. – Sean Vieira Aug 29 '21 at 01:37
31

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/

SuperShoot
  • 9,880
  • 2
  • 38
  • 55
cybaek
  • 865
  • 1
  • 8
  • 10