【Python】Flask × Render × PostgreSQL で遭遇した問題と、実際に行った技術的な解決策まとめ

未分類

Flask アプリを Render にデプロイし、PostgreSQL と接続するだけのはずが、
実際には Python バージョンの不一致、psycopg2 の非対応、SQLAlchemy の仕様変更、アプリ構造の問題など、複数の技術的課題が重なった。

この記事では、実際に遭遇したエラーと、それぞれの技術的な原因・解決策をまとめる。


## 1. Render が Python 3.13 を強制してくる問題

現象

runtime.txt に python-3.12.10 を書いても Render が無視し、
ビルドログに以下のように出る:

Using Python version 3.13.x

影響

psycopg2 が Python 3.13 に非対応のため、ImportError が発生。

ImportError: undefined symbol: _PyInterpreterState_Get

原因

Render の Python 自動判定が壊れており、runtime.txt が反映されない。

解決策

psycopg2 を捨てて psycopg3(psycopg[binary])に移行する。

requirements.txt:

psycopg[binary]

psycopg3 は Python 3.13 に対応しているため、
Render のバージョン問題を完全に回避できる。


## 2. SQLAlchemy が psycopg2 を探し続ける問題

現象

psycopg3 を入れても、SQLAlchemy が内部で psycopg2 を探して落ちる。

ModuleNotFoundError: No module named 'psycopg2'

原因

DATABASE_URL が psycopg2 用の形式になっている。

解決策

接続 URL を psycopg3 用に変更する。

Before(psycopg2)

postgresql://user:pass@host:5432/dbname

After(psycopg3)

postgresql+psycopg://user:pass@host:5432/dbname

SQLAlchemy のドライバ選択は URL で決まるため、
+psycopg を付けることで psycopg3 が使われる。


## 3. SQLAlchemy のインスタンスが二重生成されていた問題

現象

RuntimeError: The current Flask app is not registered with this 'SQLAlchemy' instance.

原因

app.py と models.py の両方で SQLAlchemy() を生成していた。

# app.py
db = SQLAlchemy(app)

# models.py
db = SQLAlchemy()  # ← これが別インスタンス

解決策

db は app.py で1つだけ作り、models.py ではそれを import する。

app.py

db = SQLAlchemy(app)
from models import Todo

models.py

from app import db

class Todo(db.Model):
    ...

これで Flask アプリとモデルが同じ db インスタンスを共有できる。


## 4. SQLAlchemy 2.x の仕様変更による text() 必須化

現象

Textual SQL expression 'SELECT 1' should be explicitly declared as text('SELECT 1')

原因

SQLAlchemy 2.0 以降、
生 SQL を実行する場合は text() で包む必要がある。

解決策

from sqlalchemy import text
db.session.execute(text('SELECT 1'))

## 5. 最終確認:PostgreSQL への接続成功

すべての修正後 /dbtest を叩いた結果:

PostgreSQL OK

これで Flask アプリが Render 上で PostgreSQL と正常に通信できることが確認できた。


## まとめ:今回の技術的ポイント

問題原因解決策
psycopg2 ImportErrorPython 3.13 非対応psycopg[binary] に移行
SQLAlchemy が psycopg2 を探すDATABASE_URL の形式postgresql+psycopg:// に変更
SQLAlchemy RuntimeErrordb インスタンスの二重生成models.py の db を app.py のものに統一
SELECT 1 エラーSQLAlchemy 2.x の仕様変更text() を使用

## 再発防止のためのベストプラクティス

  • Render は Python バージョンを強制することがある
    → psycopg3 を使うと安定する
  • SQLAlchemy の db はアプリ全体で1つだけ
  • DATABASE_URL のドライバ指定は必ず確認
  • SQLAlchemy 2.x では text() を使う
  • デプロイログは必ず最初から読む(Python バージョンが重要)
タイトルとURLをコピーしました