Reputation: 1676
I am using Flask-SQLAlchemy and Blueprints and I cannot help myself from using circular imports. I know I can write imports inside functions and make it work but it sounds nasty, I'd like to confirm with the community if there is a better way to do this.
The problem is I have a module (blueprints.py) where I declare the database and import the blueprints but those blueprints need to import the database declaration at the same time.
This is the code (excerpt of the important parts):
from application.blueprints import db
people = Blueprint('people', __name__,
template_folder='templates',
static_folder='static')
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
@people.route('/all')
def all():
users = User.query.all()
from application.apps.people.views import people
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
app.register_blueprint(people, url_prefix='/people')
I have read the documentation and the questions I found on this topic, but I still cannot find the answer I am looking for. I have found this chapter (https://pythonhosted.org/Flask-SQLAlchemy/contexts.html) where it suggest to put the initialization code inside a method but the circular import still persist.
Edit I fixed the problem using the pattern Application Factory
Upvotes: 60
Views: 28398
Reputation: 6610
I know this has been solved already, but I solved this in a slightly different way and wanted to answer in case it helps others.
Originally, my application code (e.g. my_app.py
) had this line:
db = SQLAlchemy(app)
And so in my models.py
, I had:
from my_app import db
class MyModel(db.Model):
# etc
hence the circular references when using MyModel
in my_app
.
I updated this so that models.py
had this:
# models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # note no "app" here, and no import from my_app above
class MyModel(db.Model):
# etc as before
and then in my_app.py
:
# my_app.py
from models import db, MyModel # importing db is new
# ...
db.init_app(app) # call init_app here rather than initialising db here
Upvotes: 21
Reputation: 1676
I fixed the problem with the help of the Application Factory pattern. I declare the database in a third module and configure it later in the same module in which I start the application.
This results in the following imports:
There is no circular import. It is important to make sure that the application was started and configured before calling database operations.
Here is an example application:
app.py
from database import db
from flask import Flask
import os.path
from views import User
from views import people
def create_app():
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db"
db.init_app(app)
app.register_blueprint(people, url_prefix='')
return app
def setup_database(app):
with app.app_context():
db.create_all()
user = User()
user.username = "Tom"
db.session.add(user)
db.session.commit()
if __name__ == '__main__':
app = create_app()
# Because this is just a demonstration we set up the database like this.
if not os.path.isfile('/tmp/test.db'):
setup_database(app)
app.run()
database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
views.py
from database import db
from flask.blueprints import Blueprint
people = Blueprint('people', __name__,
template_folder='templates',
static_folder='static')
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
@people.route('/')
def test():
user = User.query.filter_by(username="Tom").first()
return "Test: Username %s " % user.username
Upvotes: 94
Reputation: 756
Circular imports in Flask are driving me nuts. From the docs: http://flask.pocoo.org/docs/0.10/patterns/packages/
... Be advised that this is a bad idea in general but here it is actually fine.
It is not fine. It is deeply wrong. I also consider putting any code in __init__.py
as a bad practice. It makes the application harder to scale. Blueprints is a way to alleviate the problem with circular imports. I think Flask needs more of this.
Upvotes: 30
Reputation: 409
Serge, bring out definition of models in a separate file called models.py
.
Register blueprint in __init__.py
file of the package.
You've got circular import because blueprint file trying to import people reference from views.py
, but in views.py
you're trying to import db from blueprints.py
. And all of this is done at the top level of the modules.
You can make your project structure like this:
app
__init__.py # registering of blueprints and db initialization
mods
__init__.py
people
__init__.py # definition of module (blueprint)
views.py # from .models import User
models.py # from app import db
UPD:
For those who are in the tank:
people/__init__.py
--> mod = Module('app.mods.people', 'people')
people/views.py
--> @mod.route('/page')
app/__init__.py
--> from app.mods import people; from app.mods.people import views; app.register_blueprint(people.mod, **options);
Upvotes: 3