---
title: "DjangoとPostgreSQLの接続設定｜DATABASES定義と接続プール・固有フィールドの判断"
url: "https://www.issoh.co.jp/tech/details/17105/"
published: 2026-08-28
updated: 2026-08-28
categories: ["開発"]
publisher: "株式会社一創"
---

# DjangoとPostgreSQLの接続設定｜DATABASES定義と接続プール・固有フィールドの判断

DjangoでPostgreSQLを使う話は、settings.pyのDATABASESを数行書き換えて終わり、と説明されがちです。手元で動かすだけならそれで足ります。ただし本番で効いてくるのは、接続をどう持つか、固有の型をどこまで使うか、SQLiteから移すとき何が落ちるか、の3点でした。

しかも2026年8月時点では、フレームワークの版を上げるとデータベース側の下限も動きます。2026年8月5日に出たDjango 6.1は、対応するPostgreSQLを15以上へ引き上げました。設定の書き方に加え、どの版の組み合わせが成立するかを先に押さえる必要があります。この記事では公式ドキュメントを実測しながら、実装者が判断すべき箇所に絞って整理します。入門は[Djangoとは？Python製Webフレームワークの特徴・MVT・始め方を入門解説](https://www.issoh.co.jp/tech/details/3275/)を先に読んでください。

## まとめ：設定・接続・固有機能で決めることを先に示す

- ENGINEは`django.db.backends.postgresql`の一択です。`postgresql_psycopg2`という別名は現行の版に存在しません。
- ドライバはpsycopg 3を既定にします。psycopg2は公式に削除の見込みが明記されています。
- Django 6.1はPostgreSQL 15以上、5.2は14以上です。フレームワークの版上げがサーバ側の版上げを引き連れます。
- 接続の持ち方はWSGIなら`CONN_MAX_AGE`、ASGIならOPTIONSの`pool`です。公式はASGIで前者を止めるよう案内しています。
- OPTIONSの`pool`はpsycopg 3専用です。psycopg2では黙って無視され、効いていないことに気づけません。
- PgBouncerをトランザクションプーリングで挟むなら`DISABLE_SERVER_SIDE_CURSORS`が要ります。`iterator()`が壊れる原因です。
- PostgreSQL固有の型はArrayField・HStoreField・範囲型の3系統だけです。JSONFieldは共通フィールドで固有ではありません。
- 全文検索はGeneratedFieldとGinIndexまで標準で組めますが、configに日本語辞書が無く拡張が要ります。
- SQLiteからの移行で落ちる原因は、ほぼSQLite側の型の緩さです。移す前にデータの実体を点検します。

## DATABASES定義とENGINE・ドライバ要件を版別に確認する現在地

最小構成から確認します。ENGINEの値は1つだけです。

```
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "appdb",
        "USER": "appuser",
        "PASSWORD": "...",
        "HOST": "127.0.0.1",
        "PORT": "5432",
    }
}
```

解説記事には`django.db.backends.postgresql_psycopg2`と書かれたものが今も残っています。これは過去の別名で現行のドキュメントには載っておらず、コピー元が古いと起動時に落ちます。

### 接続情報をコードの外へ出す環境変数とサービスファイルの2通り

パスワードをsettings.pyに直書きすると、リポジトリへ混入する原因です。環境変数を読むほかに、PostgreSQL側の仕組みをそのまま使う手もあります。OPTIONSに`service`と`passfile`を指定すれば、接続先の定義をpg\_serviceファイルへ寄せられます。

```
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "OPTIONS": {
            "service": "my_service",
            "passfile": ".my_pgpass",
        },
    }
}
```

ただし公式は、テスト用途でservice名を使う構成を対応外と明記しています。テスト時だけ別指定へ切り替える前提で組んでください。

### Djangoの版ごとに変わる対応PostgreSQLとpsycopg要件

版の組み合わせは表で持つと事故が減ります。2026年8月時点の実測値です。

| Djangoの版 | 対応PostgreSQL | psycopg 3 | psycopg2 |
| -------- | ------------ | --------- | -------- |
| 5.2 LTS  | 14以上         | 3.1.8以上   | 2.8.4以上  |
| 6.0      | 14以上         | 3.1.12以上  | 2.9.9以上  |
| 6.1      | 15以上         | 3.1.12以上  | 2.9.9以上  |

6.1のリリースノートには、PostgreSQL 14の上流サポートが2026年11月に終わることを理由に15以上へ上げた、という記載です。PostgreSQL 14で止まっている環境は、Djangoを6.1へ上げる前にサーバ側を先に上げることが前提です。この順序を見落とすと、アプリの版上げ計画がデータベースの停止時間ごと巻き戻ります。サーバ側の導入は[PostgreSQLのインストール手順｜Windows・Ubuntu別の導入とinitdbで決まる文字コード](https://www.issoh.co.jp/tech/details/16963/)にまとめてあります。

ドライバはpsycopg 3を選んでください。psycopg2も動きますが、公式に削除の見込みが書かれている以上、新規案件で選ぶ理由は薄いでしょう。ドライバ側の違いは[Psycopg3とは何か｜Python用PostgreSQLデータベースアダプターの概要と主要な特徴](https://www.issoh.co.jp/tech/details/8225/)で扱っています。

### 接続ごとの往復を減らすサーバ側パラメータ3種の置き方と効果の差

Djangoは接続を張るたびに`client_encoding`をUTF8、`default_transaction_isolation`をread committed、`USE_TZ`が有効なら`timezone`をUTCへ設定します。これらをpostgresql.confかALTER ROLEで先に揃えると、接続ごとの追加クエリが不要です。短命な接続を大量に張る構成ほど差が出ました。

分離レベルを上げたい場合はOPTIONSの`isolation_level`で指定できますが、REPEATABLE READやSERIALIZABLEにすると直列化失敗の例外処理を自分で書く前提になります。アプリ全体で上げるより、必要なトランザクションだけ個別に扱うほうが破綻しにくいです。テナントごとにロールを切り替えるなら`assume_role`も使えます。

## 接続の持ち方：WSGI永続接続とASGIプールを分岐する判断軸

ここが本番で最も差が出ます。選択肢は2つで、WSGIかASGIかで決まります。

### CONN\_MAX\_AGEとヘルスチェックが効く同期構成の設定条件

`CONN_MAX_AGE`の既定値は0で、リクエストが終わるたびに接続を閉じます。gunicornなどの同期ワーカーで動かすなら、これを600秒程度にすると接続確立のコストが消えます。`None`にすれば無制限に保持しますが、デプロイのたびに古い接続が残る運用と噛み合うか確認してください。

```
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "CONN_MAX_AGE": 600,
        "CONN_HEALTH_CHECKS": True,
    }
}
```

`CONN_HEALTH_CHECKS`をTrueにすると、サーバ側で切られた接続を再利用して落ちる事故を防げます。永続接続を使うなら対で入れてください。プロセス構成は[Djangoのデプロイ手順：gunicorn＋nginx構成と本番設定・配置先の選び方](https://www.issoh.co.jp/tech/details/17087/)を参照してください。

### ASGIで永続接続を止めてpsycopgドライバ側プールへ寄せる

ASGIで動かす場合、公式は`CONN_MAX_AGE`を無効にしてドライバ組み込みのプーリングを使うよう案内しています。非同期ワーカーは1プロセスで多数のリクエストを同時に扱うため、リクエスト単位で接続を保持する前提が崩れるからです。

```
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "CONN_MAX_AGE": 0,
        "OPTIONS": {
            "pool": True,
        },
    }
}
```

細かい値を詰めるならTrueの代わりに辞書を渡し、ConnectionPoolへ渡すパラメータを書きます。前提条件が2つあります。まず`psycopg[pool]`か`psycopg-pool`を入れないと動きません。そしてpsycopg2ではこの設定が黙って無視されます。エラーが出ないため、プールが効いていない状態にしばらく気づけません。ASGIでの運用は[Django Channelsとは？ASGIとConsumer・Channel Layerの構成と採用判断](https://www.issoh.co.jp/tech/details/17095/)と併せて設計してください。

### PgBouncer併用でiterator()が壊れる理由と回避策

接続数を抑えるためにPgBouncerをトランザクションプーリングモードで挟む構成はよくあります。ここでサーバサイドカーソルが壊れます。カーソルは張った接続に紐づくのに、トランザクションが終わるたびに別の接続へ割り当てられてしまうためです。`QuerySet.iterator()`を呼んだ箇所で参照できないカーソルのエラーが出たら、まずこれを疑ってください。

回避策は3つあります。`DISABLE_SERVER_SIDE_CURSORS`をTrueにして機能ごと止める、セッションプーリングか直結の別接続で大量取得を流す、対象のQuerySetを`atomic()`で包む、のいずれかです。1件目が最も単純ですが、巨大なテーブルを全件走査するなら2件目のほうが安全でした。クエリ側の書き方は[Django ORMのクエリ設計｜遅延評価と集計の順序・生SQLへ落とす判断](https://www.issoh.co.jp/tech/details/17082/)で扱っています。

なおpsycopg 3.1.8以上ならOPTIONSに`server_side_binding`を入れてサーバサイドのパラメータバインディングへ切り替えられます。既定はクライアントサイドで、こちらもpsycopg2では無視されます。

## PostgreSQL固有フィールドを採用するかどうかの線引き

まず誤解を1つ潰します。JSONFieldはPostgreSQL固有ではありません。Django 3.1以降は`django.db.models.JSONField`として共通フィールドに統合され、`django.contrib.postgres.fields`から取り込む書き方は現行の設計ではありません。JSONBの格納設計と正規化列との境界は[PostgreSQL JSONBの使い方｜json型との格納差・GIN索引と正規化列の設計境界](https://www.issoh.co.jp/tech/details/16990/)を参照してください。

固有フィールドとして残るのは3系統です。配列を入れるArrayField、キーと値の対を入れるHStoreField、そして範囲を入れるIntegerRangeField・BigIntegerRangeField・DecimalRangeField・DateTimeRangeField・DateRangeFieldの5種になります。

### ArrayFieldで踏みやすい既定値の共有という罠と安全な定義方法

ArrayFieldの`default`に空のリテラルを直接書くと、そのオブジェクトが全インスタンスで共有されます。公式も明示的に警告している箇所です。呼び出し可能なものを渡してください。

```
class Post(models.Model):
    tags = ArrayField(models.CharField(max_length=200), default=list, blank=True)

    class Meta:
        indexes = [GinIndex(fields=["tags"])]
```

基底にForeignKeyやManyToManyField、ファイル系は指定できません。検索は`contains`、`contained_by`、`overlap`、`len`、`tags__0`形式の添字アクセスが使えます。

採否の線引きはこう置いています。配列に入れてよいのは、順序に意味があり、要素を単独の検索キーやリレーション先として扱わない場合だけです。要素で絞り込む用途が出た時点で、別テーブルへ切り出したほうが後が楽でした。6.1では、基底がJSONFieldのArrayFieldでトップレベルのNoneがJSONのnullではなくSQLのNULLとして保存されるよう変わっています。既存データを抱えたまま上げるなら、この列を先に確認してください。

### 複合型に効かないB-tree索引とGIN・GiSTの使い分け

通常のB-tree索引は複合型に効きません。`db_index=True`を付けても期待した効果は出ないため、明示的に索引の型を選ぶ設計です。ArrayFieldにはGinIndex、範囲型とHStoreFieldにはGistIndexが向きます。HStoreFieldを使う場合は`django.contrib.postgres`をINSTALLED\_APPSへ入れたうえで、サーバ側のhstore拡張作成が必要です。マイグレーションでの拡張作成と適用手順は[Django migrateの実務｜makemigrationsからの流れと本番適用・巻き戻し](https://www.issoh.co.jp/tech/details/17080/)にまとめてあります。

範囲型は既定の境界が下限を含み上限を含まない形です。PostgreSQL側が正規化するため、同じ値を上下限にした範囲は空として扱われます。予約枠や有効期間を範囲型で持てば重なり判定を排他制約へ寄せられますが、境界の扱いを仕様として先に決めないと、後から集計値が合わなくなります。

## 全文検索をDjango標準機能だけで賄える範囲と日本語処理の壁

Djangoは`django.contrib.postgres.search`で全文検索の道具を一通り持っています。単一フィールドなら`body_text__search`で済み、複数フィールドを束ねるならSearchVectorを使います。クエリ側はSearchQueryでplain・phrase・raw・websearchを切り替えられ、並び順はSearchRank、ハイライトはSearchHeadlineです。6.0で追加されたLexemeなら、信頼できない入力も演算子込みで安全に組み立てられます。

### 数百件を超えた全文検索で索引を持たせる3通りの実装方法と選び方

公式は、レコードが数百件を超えると素のSearchVectorでは性能が出ないと明記しています。用意されている手は3つで、GinIndexの関数索引を張る、SearchVectorFieldの列を持って更新する、GeneratedFieldにSearchVectorを入れてPostgreSQL側に維持させる、という順に運用が軽くなります。

```
class Entry(models.Model):
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    search_vector = models.GeneratedField(
        expression=SearchVector("headline", weight="A")
        + SearchVector("body_text", weight="B"),
        output_field=SearchVectorField(),
        db_persist=True,
    )

    class Meta:
        indexes = [GinIndex(fields=["search_vector"])]
```

更新のたびに列を書き戻す処理が要らないぶん、GeneratedFieldが扱いやすいです。6.1ではPostgreSQL 18以上で`db_persist=False`の仮想列にも対応しました。

### configに日本語辞書が無いという全文検索設計の境界線と代替策

ここが判断の分かれ目です。SearchVectorとSearchQueryの`config`には`english`や`french`といった言語辞書を指定しますが、公式ドキュメントの一覧に日本語の辞書はありません。日本語は語の区切りが空白で示されないため、既定の設定では意図した語に分割されないまま索引が作られます。

日本語で実用的な全文検索を出すなら、pg\_bigmやPGroongaのようなサーバ側の拡張を入れ、Djangoからは生SQLか拡張の演算子経由で呼ぶ形になります。Django標準で完結する範囲は、英語圏のコンテンツか、TrigramSimilarityで曖昧一致を取る用途までです。pg\_trgm拡張なら人名や商品名の表記ゆれ吸収に使えますが、これは全文検索ではなく類似度検索だと割り切ってください。要件定義でここを見誤ると、検索機能だけ作り直しになります。

## SQLiteからPostgreSQLへ移す手順と落ちやすい箇所

開発をSQLiteで始めて本番前にPostgreSQLへ移す進め方は、今も現実的です。ただし移行は単純なデータ移送になりません。

### 空のPostgreSQLデータベースへmigrateしてから流し込む順序

手順は4段です。PostgreSQL側にデータベースとロールを作る、settingsの接続先を切り替える、空のデータベースへ`migrate`でスキーマを作る、最後にデータを流し込みます。取り出しはdumpdataですが、そのまま全件出すとcontenttypesとpermissionが衝突します。

```
python manage.py dumpdata --natural-foreign --natural-primary -e contenttypes -e auth.Permission --indent 2 --output data.json

python manage.py migrate
python manage.py loaddata data.json
```

自然キーを使うのは、両側でcontenttypesのIDが一致しないためです。IDで参照したまま流し込むと、別のモデルに紐づいた状態で取り込まれます。数十万行を超えるとJSON経由は現実的でなくなるので、pgloaderのような専用ツールへ切り替えてください。

### SQLiteの型の緩さに由来する移行時の4つの失敗と対処方法

loaddataが落ちる原因はほぼ決まっています。SQLiteが宣言した型を強制しないため、PostgreSQLで通らないデータが混ざるのです。

| 落ちる箇所      | 原因              | 先に取る手            |
| ---------- | --------------- | ---------------- |
| varchar長超過 | max\_lengthが未強制 | 投入前に長さを集計        |
| 日時列の不正値    | 文字列やナイーブ値       | 型と時差を点検          |
| 数値列の文字列    | 型宣言が効かない        | 該当行を洗い出す         |
| 主キー重複      | シーケンス未更新        | sqlsequencereset |

4件目は移行に限らず起きます。主キーを明示指定して作るとシーケンスが進まず、次の自動採番で重複エラーになります。`sqlsequencereset`が出力するSQLを流して戻してください。

もう1つ挙動が変わるのが大文字小文字の扱いです。SQLiteのLIKEはASCII範囲で区別しないため、`__contains`で書いた検索がPostgreSQLでは区別する動きに変わります。検索が急に当たらなくなったと言われる原因はたいていここで、`__icontains`への置き換えが要ります。セッションをデータベースに置いている場合の移送は[Djangoのセッション設定：保存先5種の選び分けとRedis構成の判断](https://www.issoh.co.jp/tech/details/17102/)も併せて確認してください。

稼働中の業務アプリをSQLiteのまま抱えていて、停止時間を取れる範囲で移したい場合は、データ点検から切り替え計画まで一括で引き受けられます。[業務用・Webアプリ開発](https://www.issoh.co.jp/service/apps/business/)では、既存アプリのデータベース移行と移行後の運用設計まで対応しています。

## MySQLではなくPostgreSQLを選ぶ判断軸の置きどころ

製品同士の比較は[PostgreSQLとMySQLの違いを徹底比較｜性能・データ型・全文検索・移行と使い分け【2026年版】](https://www.issoh.co.jp/tech/details/3545/)に譲り、Django側から見て分岐する点だけを挙げます。

### Django側の機能差から見たPostgreSQL採用条件と見送り場面

PostgreSQLを選ぶ条件は3つに絞れます。1つ目は固有フィールドと排他制約を使う要件があるとき、2つ目はDDLをトランザクション内で巻き戻したいとき、3つ目はGeneratedFieldや部分索引を前提に設計するときです。特に2つ目はマイグレーション運用に直結します。PostgreSQLはDDLをトランザクションに含められるため、失敗したマイグレーションが中途半端な状態で残りません。MySQLは暗黙コミットが入るぶん、失敗時に手で片づける運用になります。

逆に見送ってよい場面もはっきりしています。既存のPostgreSQLが14で止まりサーバ側の版上げ予定が立たないなら、6.1への追随を諦めて5.2 LTSに留まる判断が成立します。チームがMySQLしか実績を持たず、使いたい機能がJSONFieldと通常の索引だけなら、PostgreSQLへ寄せる利得はほとんど出ません。判断が割れるのは全文検索を求められたときですが、日本語ではどちらでも拡張が要るため、この一点だけで選ぶ理由にはなりません。

## よくある質問

### ENGINEはpostgresqlとpostgresql\_psycopg2のどちらを書きますか？

`django.db.backends.postgresql`だけです。`postgresql_psycopg2`は過去に存在した別名で、現行のドキュメントには載っていません。psycopg 3を使う場合もENGINEの値は変わらず、インストールしたドライバをDjangoが判別します。

### CONN\_MAX\_AGEとOPTIONSのpoolは併用できますか？

併用する構成は想定されていません。ASGIで動かすなら`CONN_MAX_AGE`を無効化してドライバ側のプールへ寄せる、というのが公式の案内です。WSGIの同期ワーカーであれば`CONN_MAX_AGE`で足ります。両方を有効にすると接続の寿命を管理する主体が二重になり、切断済み接続の再利用が読めなくなります。

### PgBouncerを挟むとiterator()でエラーになるのはなぜですか？

トランザクションプーリングモードでは、トランザクションが終わるたびに別の接続へ割り当てが変わります。サーバサイドカーソルは張った接続に紐づくため、次に参照しようとした時点で見つからずエラーになります。`DISABLE_SERVER_SIDE_CURSORS`をTrueにするか、大量取得だけセッションプーリングの別接続へ逃がしてください。

### JSONFieldはdjango.contrib.postgresから取り込みますか？

いいえ。Django 3.1以降は`django.db.models.JSONField`が正しい所在で、PostgreSQL固有ではありません。古い記事の取り込み文をそのまま写すと動きません。PostgreSQL側でjsonbとして格納される点は変わらないため、索引や正規化列との設計判断は別途必要になります。

### 日本語の全文検索はDjangoの標準機能だけでできますか？

実用の水準では難しいです。`config`に指定できる言語辞書に日本語が無く、既定の設定では語の区切りが取れません。pg\_bigmやPGroongaといったサーバ側の拡張を入れる前提で設計してください。表記ゆれの吸収だけならpg\_trgmとTrigramSimilarityで足りる場合もあります。

## 関連記事

- [Djangoとは？Python製Webフレームワークの特徴・MVT・始め方を入門解説](https://www.issoh.co.jp/tech/details/3275/)：フレームワーク全体の位置づけです。
- [PostgreSQLとMySQLの違いを徹底比較｜性能・データ型・全文検索・移行と使い分け【2026年版】](https://www.issoh.co.jp/tech/details/3545/)：製品同士の比較はこちらです。
- [Django ORMのクエリ設計｜遅延評価と集計の順序・生SQLへ落とす判断](https://www.issoh.co.jp/tech/details/17082/)：クエリ側の設計です。
- [Django migrateの実務｜makemigrationsからの流れと本番適用・巻き戻し](https://www.issoh.co.jp/tech/details/17080/)：スキーマ適用の手順です。
- [PostgreSQL JSONBの使い方｜json型との格納差・GIN索引と正規化列の設計境界](https://www.issoh.co.jp/tech/details/16990/)：JSON列の設計境界です。
- [Psycopg3とは何か｜Python用PostgreSQLデータベースアダプターの概要と主要な特徴](https://www.issoh.co.jp/tech/details/8225/)：ドライバ側の詳細です。

---

出典: [DjangoとPostgreSQLの接続設定｜DATABASES定義と接続プール・固有フィールドの判断](<https://www.issoh.co.jp/tech/details/17105/>)（株式会社一創）
