Flask アプリケーションが大きくなると circular import(循環インポート)の問題に遭遇しやすい。モジュール A が B をインポートし、B が A をインポートすると、ImportError や AttributeError が発生する。
循環インポートが起きる典型例
# app.py
from flask import Flask
from views import main_bp # views.py をインポート
app = Flask(__name__)
app.register_blueprint(main_bp)
# views.py
from flask import Blueprint
from app import app # app.py をインポート → 循環
main_bp = Blueprint('main', __name__)
@main_bp.route('/')
def index():
return app.config['APP_NAME'] # app を使いたい
この構成では app.py → views.py → app.py の循環が発生する。
解決策 1: current_app を使う
app を直接インポートせず、current_app を使う。
# views.py
from flask import Blueprint, current_app
main_bp = Blueprint('main', __name__)
@main_bp.route('/')
def index():
return current_app.config['APP_NAME'] # リクエスト中は current_app が使える
解決策 2: アプリケーションファクトリパターン
app インスタンスを関数内で生成し、循環を避ける。
# app/__init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
from app.views import main_bp # 関数内でインポート
app.register_blueprint(main_bp)
return app
# app/views.py
from flask import Blueprint, current_app
main_bp = Blueprint('main', __name__)
@main_bp.route('/')
def index():
return current_app.config['APP_NAME']
解決策 3: 遅延インポート
必要になった時点でインポートする。
# models.py
def get_user_model():
from app.models.user import User # 関数内でインポート
return User
推奨されるディレクトリ構成
# extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # app なしで初期化
# __init__.py
from flask import Flask
from app.extensions import db
def create_app():
app = Flask(__name__)
db.init_app(app) # 後から app を渡す
return app
この構成なら、models や views から extensions.py の db をインポートしても循環は発生しない。