Flask circular dependency
Asked Answered
X

2

6

I am developing a Flask application. It is still relatively small. I had only one app.py file, but because I needed to do database migrations, I divided it into 3 using this guide:

https://realpython.com/blog/python/flask-by-example-part-2-postgres-sqlalchemy-and-alembic/

However, I now can't run my application as there is a circular dependency between app and models.

app.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import render_template, request, redirect, url_for
import os

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DB_URL']
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.debug = True

db = SQLAlchemy(app)

from models import User

... routes ...    

if __name__ == "__main__":
  app.run()

models.py:

from app import db
class User(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  username = db.Column(db.String(80), unique=True)
  email = db.Column(db.String(120), unique=True)

  def __init__(self, username, email):
    self.username = username
    self.email = email

  def __repr__(self):
    return self.username

manage.py:

from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import app, db

migrate = Migrate(app, db)
manager = Manager(app)

manager.add_command('db', MigrateCommand)

if __name__ == "__main__":
  manager.run()

They are all in the same directory. When I try to run python app.py to start the server, I receive an error which definitely shows a circular dependency (which is pretty obvious). Did I make any mistakes when following the guide or is the guide wrong? How can I refactor this to be correct?

Thanks a lot.

EDIT: Traceback

Traceback (most recent call last):
  File "app.py", line 14, in <module>
    from models import User
  File "/../models.py", line 1, in <module>
    from app import db
  File "/../app.py", line 14, in <module>
    from models import User
ImportError: cannot import name User
Xenos answered 28/3, 2017 at 18:49 Comment(0)
O
9

I propose the following structure:

# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...


# app/app.py
from app.extensions import db

def create_app(config_object=ProdConfig):
    app = Flask(__name__.split('.')[0])
    app.config.from_object(config_object)
    register_extensions(app)
    ...

def register_extensions(app):
    db.init_app(app)
    ...

# manage.py
from yourapp.app import create_app
app = create_app()
app.debug = True
...

In this case, database, app, and your models are all in separate modules and there are no conflicting or circular imports.

Oversell answered 28/3, 2017 at 18:59 Comment(0)
I
7

I chased this for a few hours, landing here a few times, and it turned out I was importing my page modules (the ones holding the @app.route commands) before the line where the app was created. This is easy to do since import commands tend to be placed at the very beginning, but it doesn't work in this case.

So this:

# app/__init__.py
print("starting __init__.py")

from flask import Flask
from flask import render_template
import matplotlib.pyplot as plt
import numpy as np
import mpld3


app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')

from . import index
from . import simple

app.run(threaded=False)

print("finished __init__.py")

Instead of having all imports on top.

Placing this here because this has to be a common error for casual flask users to encounter and they are likely to land here. I have hit it as least twice in the last couple of years.

Integument answered 27/8, 2020 at 8:52 Comment(1)
Yes, this was indeed the solution in my case, now I fully understand the mechanism of the imports. Thanks Mike.Pomade

© 2022 - 2024 — McMap. All rights reserved.