Djangoで非NULLな外部キーを追加するときに出るエラーの対処法：You are trying to add a non-nullable field... の解説

## ０．はじめに
DjangoでWebアプリケーションを開発していると、後からモデルにフィールド（特に外部キーや必須項目）を追加したくなることがあります。
その際、マイグレーションを実行しようとすると、以下のようなエラーメッセージが表示されて処理が中断されることがあります：

```
You are trying to add a non-nullable field 'user' to todo without a default; we can't do that (the database needs something to populate existing rows).

Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Ignore for now. Existing rows that contain NULL values will have to be handled manually, for example with a RunPython or RunSQL operation.
 3) Quit and manually define a default value in models.py.

Select an option:
```

これは、すでに存在しているデータに対して「NULLを許容しないフィールド（non-nullable field）」を追加しようとした場合に起きるエラーです。

簡単に言えば、「既存のデータにこの新しいカラムをどう埋めればいいのか分からない」とDjangoに言われているわけです。

たとえば、Todoアプリで最初に`Todo`モデル（テーブル）を作成した後、`User`モデルを定義して、それに関連付ける外部キー（`user = models.ForeignKey(...)`）を追加しようとした場合などに、このエラーが発生します。

このエラーが出たとき、「どの選択肢を選ぶべきか」「そもそもなぜこのような確認が求められるのか」が分からず、戸惑う方も多いと思います。

そこで本記事では、このエラーメッセージの意味を日本語で丁寧に解説した上で、代表的な3つの対処法について、それぞれのメリットや注意点を含めて整理します。
これは、Djangoでのマイグレーション設計やデータ整合性の考え方にも関わる内容であり、中級者以上にとっても再確認する価値のあるトピックです。

## １．日本語訳と解説
まずは、表示されたエラーメッセージの内容を文ごとに分解し、日本語訳とその意味を見ていきます。

### （１）エラーメッセージの日本語訳（全文）
>あなたは `todo` テーブルに、NULLを許容しない `user` フィールドを、デフォルト値なしで追加しようとしています。それはできません（データベースは、既存の行を埋めるための値を必要とします）。
>
>修正方法を選んでください：
>1）一時的なデフォルト値を今すぐ指定してください（その値が、既存のすべての行に設定されます）
>2）今は無視してください。NULL値を含む既存の行は、たとえば RunPython や RunSQL を使って手動で対応する必要があります
>3）中断して、models.py にデフォルト値を手動で定義してください

***

### （２）各文のポイント解説
#### Ａ．`non-nullable field` と `without a default`
- NULLを許容しない（必須）項目を、既存のテーブルに追加しようとする場合、既存のデータにその値が入っていないと整合性が取れません。
- しかも、default（初期値）が定義されていないため、Djangoは「どうすればいいのか判断できない」という状態です。

#### Ｂ．「データベースは既存の行を埋めるための値を必要とします」
- NULLを許容しない以上、既存のレコード全てに有効な値を入れなければならないという制約があります。
- そのため「追加だけ」では完了せず、マイグレーション時点での値の補填方法が求められるというわけです。

#### Ｃ．選択肢の意味（簡潔な概要）
| 選択肢 | 意味 | 使用例・注意点 |
|:-|:-|:-|
| 1）一時的デフォルト | 既存データにだけ特定の値を入れる | 過去のデータは全て「仮のユーザー」で統一、など |
| 2）手動対応 | スクリプトで後から補完 | 既存データに意味のある値を付与したい場合 |
| 3）models.pyで定義 | defaultを明示して以降も適用 | 新規データにも同じ値が使われるので要注意

このように、このエラーは「Djangoが既存データをどう埋めればよいか判断できない」というごく実務的な事情に基づいて発生しています。
次章では、それぞれの対処法を具体例とともに比較し、どのようなケースでどの手段を選ぶべきかを解説します。

## ２．対処法
このエラーに対して、Djangoは3つの選択肢を提示しています。それぞれの方法に、適した場面・注意点があります。ここでは、選択肢の意味と使いどころを整理して解説します。

### （１）選択肢１：一時的なデフォルト値を指定する
>1）一時的なデフォルト値を今すぐ指定してください（その値が、既存のすべての行に設定されます）

#### Ａ．概要
マイグレーション時に、**既存のすべてのレコードに対して一括で値を設定する方法**です。
この値は**一度だけ適用される**ものであり、`models.py` には残りません。

#### Ｂ．向いているケース
- 「過去データには特定の仮ユーザーを割り当てればよい」など、**既存データに共通の値で問題ない**場合
- 例えば、`user = models.ForeignKey(User, ...)` を追加する際に、既存データにはダミーユーザー（`id=1`など）を入れればよいと判断できる場合

#### Ｃ．注意点
- 指定した値が**実在するレコードと整合している必要がある**（例：存在しないUserのIDを指定すると、整合性エラーになる）
- 新しいレコードにはこのデフォルト値は**使われない**。以降のデータ作成時は明示的な入力が必要

***

### （２）選択肢２：無視して後から手動で処理する
>2）今は無視してください。NULL値を含む既存の行は、たとえば RunPython や RunSQL を使って手動で対応する必要があります

#### Ａ．概要
マイグレーションファイル自体は生成されますが、**既存のデータにはNULLが入る**状態になります。
この状態を**後からRunPythonなどで埋める処理**を書く前提です。

#### Ｂ．向いているケース
- 既存データの内容に応じて、**個別に違う値を入れたい場合**（例：作成者が誰かをログや他テーブルから推測して埋めたい）
- 一旦マイグレーションを通して、後から`RunPython`で処理するスクリプトを書く方が合理的な場合

#### Ｃ．注意点
- NULLのままだとエラーになるため、**マイグレーション適用後に即座に処理を書く必要がある**
- NULLを許さない制約があるため、**補完前にレコード作成や表示が失敗するリスク**がある

***

### （３）選択肢３：models.pyにdefaultを明示的に記載する
>3）中断して、models.py にデフォルト値を手動で定義してください

#### Ａ．概要
`models.py`の対象フィールドに、`default=◯◯` を追加して、マイグレーションを再実行する方法です。
```models.py
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
```

#### Ｂ．向いているケース
- 「新しいレコードにもこの値を使い続ける設計」で問題ない場合
- 全ての新規データにおいて、同じ初期値で運用する方針であれば、モデル側でdefaultを明記しておく方が自然

#### Ｃ．注意点
- defaultを明記すると、**今後もその値が自動で使われ続ける**ため、運用上意図しないデータが蓄積されるリスクがある。
- 「仮の値」や「一時的な代替値」を入れたいだけであれば、**むしろ選択肢１の方が安全**

***

### （４）まとめ：どの選択肢を選ぶべきか？
| ケース | 選択肢 | コメント |
|:-|:-|:-|
| 既存データは一律で仮の値を入れてよい | ① | 一時的なdefaultで済む |
| 既存データごとに別々の値を入れたい | ② | 後から補完処理を書くべき |
| これから作成するデータも同じ初期値で良い | ③ | モデル側にdefaultを定義 |

エラーの根本は「既存のデータにNULLを許さないカラムをどう埋めるか？」というデータ整合性の問題です。
本質は「どのように既存データを処理すべきかの判断」にあります。

## ３．未然に防ぐための設計上の工夫
このようなエラーは、DjangoやORMの仕組みによる「自動的な整合性チェック」によって表面化しますが、設計段階で少し意識するだけで未然に防ぐことができます。
ここでは、実務上よく使われる2つの工夫を紹介します。

### （１）nullable → データ補完 → 非nullable という段階的対応
いきなり`nullable=False`で新しいフィールドを追加すると、今回のようなエラーが出ます。
このため、「**段階的に制約を強める**」というアプローチが安全です。

#### 手順の例
1\. 最初は、nullable=Trueでフィールドを追加：
```models.py
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
```

2\. マイグレーションを適用後、生成されたマイグレーションファイルで既存データを補完：
```000X_add_user_field.py
def populate_user(apps, schema_editor):
    Todo = apps.get_model('yourapp', 'Todo')
    User = apps.get_model('auth', 'User')
    default_user = User.objects.get(id=1)
    for todo in Todo.objects.filter(user__isnull=True):
        todo.user = default_user
        todo.save()
```

3\. 全てのデータが埋まったことを確認後、nullable=Falseに変更して再マイグレーションする

***

### （２）defaultを付けるかどうかの設計基準を明確に
`default=◯◯` をフィールドに指定することでエラーは回避できますが、それは常にその値が入るように設計することを意味します。
したがって、以下の観点でdefaultを付けるべきかを判断すべきです：
| 判断軸 | defaultを付けるべき | defaultを避けるべき |
|:-|:-|:-|
| 値に意味があるか | 「status=未完了」のような初期状態 | 「仮のユーザーID」など一時的な逃げ |
| 今後も使うか | データ作成時に省略しても問題ない | 後から手動で置き換える前提の値 |

### （３）テスト用のマイグレーション環境を用意する
実際のDBで適用する前に、ローカルでスキーマ変更とマイグレーション操作を検証できる環境（例：SQLiteなど）を用意しておくことで、こうしたエラーを事前に把握できます。
- `makemigrations`直後に`sqlmigrate`でSQLを確認する
- `migrate`前にモデルの状態とマイグレーションファイルを見直す

以上のように、設計段階から意識しておくことで、Djangoのマイグレーション時の「つまずきポイント」は大幅に減らすことが可能です。

## ４．おわりに
本記事では、Djangoで「NULLを許容しないフィールドを既存テーブルに追加したとき」に発生する代表的なエラーと、その意味、対処法、そして未然に防ぐための設計上の工夫について整理しました。

このエラーは一見すると「よくあるマイグレーションのつまずき」に見えるかもしれませんが、実際にはデータベースの整合性と、アプリケーション設計の思想に関わる重要な問題です。

Djangoは柔軟なフレームワークであるがゆえに、フィールドの追加や制約の変更といった操作にも、明確な意図と手順が求められます。  
設計の初期段階から「マイグレーションにどう影響するか」を意識できると、トラブルの回避にもつながります。

「参考になった！」と思ったらいいねしていただけると励みになります！
また、気になる点があればコメントで教えていただけると嬉しいです。