57

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

application.apps.people.views.py

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

application.blueprints.py

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

S182
  • 1,626
  • 1
  • 11
  • 10

4 Answers4

92

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:

  • database.py → app.py
  • views.py → app.py
  • database.py → views.py

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
MJeffryes
  • 458
  • 3
  • 14
S182
  • 1,626
  • 1
  • 11
  • 10
30

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.

dexity
  • 756
  • 7
  • 7
  • 2
    Putting the core app code in an app/__init__.py file seems to be pretty common: https://www.digitalocean.com/community/tutorials/how-to-structure-large-flask-applications https://github.com/mitsuhiko/flask/wiki/Large-app-how-to and even in the O'Reily Flask Web Applications book – Erik Mar 17 '16 at 20:29
  • 2
    Could you offer some pointers for how Blueprints help avoid circulars? – Samuel Harmer Apr 03 '17 at 17:25
21

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
Sam
  • 5,997
  • 5
  • 46
  • 66
  • 2
    What happens if you multiple model files? Would they all have to `from models import db`? – Paymahn Moghadasian May 11 '18 at 20:53
  • 3
    @PaymahnMoghadasian I have multiple model classes in `models.py`. If you had multiple files with models in, that would be more complicated - you'd need to move `db = SQLAlchemy()` to a separate file which both the models and the application imported from, I think (but note that I've not tried this). – Sam May 17 '18 at 10:05
  • 2
    This works in the simple case that you described in your answer but it would become quite problematic if you were to have multiple models defined in separate files. – pfabri Mar 22 '19 at 12:07
3

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

Mike
  • 399
  • 3
  • 4
  • Thanks. Indeed I have the models definition separated but for this circular import dilemma it makes no difference because I have to import models from the view module. What are the reasons to register the blueprint in init.py? – S182 Apr 08 '14 at 09:42
  • @Serge, I've corrected my answer and gave you just about right structure for your project. – Mike Apr 08 '14 at 09:49
  • It didn't work, because @people.route('/all') is never initialized with the view function if I put view and blueprint separated. – S182 Apr 09 '14 at 05:24
  • 1
    Ohh.. Updated my answer, I advise you before you write to read at least a little documentation... – Mike Apr 09 '14 at 09:02
  • The update also contains a circular import: people/views imports db from app/__init__ and app/__init__ imports people/views. Also the use of Module is not necessary in this case. Thanks anyway. What I am doing now is to encapsulate the app initialization inside a create_app() pushing db outside and using flask.current_app to access the app values in runtime. – S182 Apr 09 '14 at 16:57
  • You should register blueprints and import views after db creation at `app/__init__.py` and this helps you to avoid circular import. Anyway, encapsulating creation in func is good approach, especially when you will test it later. – Mike Apr 10 '14 at 09:15
  • I know that, although it works it is still a circular import. – S182 Apr 11 '14 at 09:27