TL;DR
バックグラウンドスレッド、スクリプト、Celeryタスクなど、Flaskがコンテキストを準備していない場所で current_app、g、またはその他のFlaskコンテキストローカルを呼び出しています。コンテキストを自分でプッシュしてください:
with app.app_context():
# your code here
result = db.session.query(User).all()
スレッド、Celeryタスク、スケジュールジョブはそれぞれ少し異なるアプローチが必要です。状況に合ったセクションへジャンプしてください。
エラーの全文
RuntimeError: Working outside of application context. This typically means that you attempted to use functionality that needed to interface with the current application object in some way.
Flaskは多くの場合、正確なオブジェクト(current_app、g、url_for()、またはFlask-SQLAlchemyモデル)を特定する2行目を表示します。デバッグはその2行目から始めましょう。
根本原因
内部的に、Flaskはスレッドごとにコンテキストローカルスタックを保持しており、現在のアプリと現在のリクエストを格納しています。すべての受信HTTPリクエストは、アプリケーションコンテキストとリクエストコンテキストの2つをスタックにプッシュします。ビュー関数はその両方を自動的に受け取ります。
しかし、そのリクエストのライフサイクルの外に出ると、スタックは空になります。よくある原因:
threading.Threadで生成されたバックグラウンドスレッド- CeleryまたはRQのワーカータスク
- モデルをインポートするスタンドアロンPythonスクリプト
- アプリコンテキストのフィクスチャがない
pytestテスト - APSchedulerまたはFlask-APSchedulerのスケジュールジョブ
flask <command>経由ではなく、通常のPythonスクリプトとして実行されたFlask CLIコマンド
これらはいずれもFlaskのリクエストライフサイクルを通らないため、スタックには何も積まれていません。
修正1 — app.app_context() でラップする(最もよくある方法)
app オブジェクトへの直接参照がありますか?コンテキストマネージャを使えば2行で解決します:
from myapp import create_app
app = create_app()
with app.app_context():
from myapp.models import import User
users = User.query.all()
print(users)
スタンドアロンスクリプト、管理タスク、単発のデータマイグレーション — このパターンはそのすべてに対応します。
修正2 — バックグラウンドスレッド
コンテキストローカルはスレッド境界を越えません。新しいスレッドは空のスタックからスタートするため、スレッド関数の内部でフレッシュなコンテキストをプッシュする必要があります:
import threading
from flask import current_app
def background_job(app):
with app.app_context():
# current_app はここで動作する
print(current_app.config["DATABASE_URI"])
# current_app ではなく実際の app オブジェクトを渡す
thread = threading.Thread(target=background_job, args=(app,))
thread.start()
current_app ではなく app を必ず渡してください。 current_app はアクティブなコンテキスト内でのみ解決されるプロキシです。別のスレッドに渡すと即座にエラーになります。
修正3 — Celeryタスク
CeleryワーカーはFlaskコンテキストが全くない別プロセスです。最もクリーンな修正方法は、すべてのタスク呼び出しをアプリコンテキストでラップするカスタム Task 基底クラスを使うことです:
# celery_app.py
from celery import Task
from myapp import create_app
class FlaskTask(Task):
def __call__(self, *args, **kwargs):
with app.app_context():
return super().__call__(*args, **kwargs)
app = create_app()
celery = app.extensions["celery"]
celery.Task = FlaskTask
Flask 2.3以降には組み込みのCelery連携機能があります。init_celery(app) ファクトリパターンについては公式のFlask + Celery チュートリアルを参照してください — この配線を自動的に処理してくれます。
修正4 — pytestテスト
フィクスチャ経由でコンテキストをプッシュし、その内部から yield します。フィクスチャを受け取るすべてのテストはライブなアプリコンテキスト内で実行されます:
# conftest.py
import pytest
from myapp import create_app, db as _db
@pytest.fixture()
def app():
app = create_app({"TESTING": True, "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:"})
with app.app_context():
_db.create_all()
yield app
_db.drop_all()
@pytest.fixture()
def client(app):
return app.test_client()
コンテキストブロック内の yield がポイントです — セットアップ、テスト本体、ティアダウンのすべてが同一のアクティブコンテキスト内で実行されます。
修正5 — APScheduler / Flask-APSchedulerジョブ
スケジュールジョブはリクエストサイクルの外で実行されます。Flask-APSchedulerでは、scheduler.app 経由でアプリにアクセスし、ジョブ関数内でフレッシュなコンテキストをプッシュします:
from flask_apscheduler import APScheduler
scheduler = APScheduler()
@scheduler.task("cron", id="cleanup", hour=2)
def cleanup_job():
with scheduler.app.app_context():
from myapp.models import Session
Session.query.filter(Session.expired == True).delete()
db.session.commit()
修正6 — Flask CLIコマンド
@app.cli.command で登録されたコマンドは、flask <command> 経由で実行するとアプリコンテキストが自動的にプッシュされます。追加のセットアップは不要です。
落とし穴: python seed_db.py でファイルを直接実行すると Flask CLI を完全にスキップするため、コンテキストはプッシュされません。必ずCLIエントリポイントを使用してください:
@app.cli.command("seed-db")
def seed_db():
# アプリコンテキストはすでにアクティブ — 追加プッシュ不要
db.session.add(AdminUser(email="admin@example.com"))
db.session.commit()
click.echo("Done.")
python seed_db.py ではなく flask seed-db で実行してください。
確認方法
修正を適用したら、失敗していたコードパスを再実行してください。RuntimeError が消えているはずです。コンテキストブロック内に簡単なサニティチェックを追加して確認しましょう:
with app.app_context():
from flask import current_app
assert current_app._get_current_object() is app
print("App context OK:", current_app.name)
Flask-SQLAlchemyの場合、セッションも生きていることを確認します:
with app.app_context():
from myapp.models import User
count = User.query.count()
print(f"{count} users in DB — context is live")
クイックリファレンス
シナリオ修正方法
スタンドアロンスクリプト`with app.app_context():`
バックグラウンドスレッド`app` をスレッドに渡し、内部でプッシュ
Celeryタスクカスタム `Task` 基底クラスまたはFlask 2.3+ヘルパー
pytest`with app.app_context(): yield` を使ったアプリフィクスチャ
APSchedulerジョブ`with scheduler.app.app_context():`
Flask CLIコマンド`flask <command>` を使用 — コンテキストは自動

