Circular import of db reference using Flask-SQLAlchemy and Blueprints

眉间皱痕 提交于 2019-11-27 12:03:56
S182

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

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.

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

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:

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:

from models import db, MyModel  # importing db is new

# ...

db.init_app(app)  # call init_app here rather than initialising db here
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!