疑念は探究の動機であり、探究の唯一の目的は信念の確定である。

数学・論理学・哲学・語学のことを書きたいと思います。どんなことでも何かコメントいただけるとうれしいです。特に、勉学のことで間違いなどあったらご指摘いただけると幸いです。 よろしくお願いします。くりぃむのラジオを聴くこととパワポケ2と日向坂46が人生の唯一の楽しみです。

Djangoのマイグレーションエラーについて

概要

Djangoにおいて、あるモデルを修正して、python manage.py makemigrationsすると、次のエラーが生じることがある。

% python manage.py makemigrations
It is impossible to add a non-nullable field 'profile' to mymodel without specifying a default. This is because 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) Quit and manually define a default value in models.py.

このエラーの対処法をまとめる。

結論

次のようなモデルがあるとする。

class MyModel(models.Model):
    """My model"""
    first_name = models.CharField(max_length=10, null=True, blank=True)
    last_name = models.CharField(max_length=10)
  1. 既存のフィールドを変更する場合: e.g., first_namenull=Trueblank=Trueを削除する場合。

    --> 1)を実行する。

  2. 新しいフィールドを追加する場合: e.g., profileフィールドを追加する場合。

    --> 2)を実行する。オプションnull=Truedefault='hoge'などを追加したフィールドを追加する。profile = models.CharField(max_length=50, null=True)

  3. 新しいフィールドをオプションなしで追加する場合: e.g., オプションなしのprofileフィールドを追加する場合。

    --> 上記の2と1を実行する。

前提知識

  • Djangoを知っていることです。ここに書かれてあるコードをそのまま移しても再現されません。ですので想定読者は自分で読み替える程度の知識と経験があることです。

1. 既存のフィールドを変更する場合

  • mymodel.py
"""My model"""

from django.db import models

class MyModel(models.Model):
    """My model"""
    first_name = models.CharField(max_length=10, null=True, blank=True)
    last_name = models.CharField(max_length=10)
  • migrations/0004_mymodel.py
  • このファイルはpython manage.py makemigrationsで作成されたものです。
# Generated by Django 5.0.4 on 2024-04-30 03:58

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0003_store'),
    ]

    operations = [
        migrations.CreateModel(
            name='MyModel',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('first_name', models.CharField(max_length=10, null=True, blank=True)),
                ('last_name', models.CharField(max_length=10)),
            ],
        ),
    ]

このとき管理者画面からデータを追加しました。first_nameフィールドはnull=Trueおよびblank=Trueですので、first_nameが空欄のデータを追加することができます。

ここからmodels.CharField(max_length=10)に変更するとします。

そのままfirst_name = models.CharField(max_length=10)と変更して、マイグレーションファイルを作成するとエラーが生じます。

class MyModel(models.Model):
    """My model"""
    first_name = models.CharField(max_length=10)
    last_name = models.CharField(max_length=10)
python manage.py makemigrations

It is impossible to change a nullable field 'first_name' on mymodel to non-nullable without providing a default. This is because 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.

このとき変更したデータベース(MyModel)に空のフィールドがあるかどうかで対応が変わります。つまりデータベースのレコードに空のフィールドがある場合と、それ以外(データベースにレコードが存在しないかまたは存在しても空のフィールドがない場合)で修正方法が異なります。まずは後者の空のフィールドがない場合の修正方法です。

1.1 空のフィールドがない場合

コマンドで2)を選択すれば問題ありません。

python manage.py migrateしてもエラーが生じません。これでフィールドの変更の完了です。

1.2 空のフィールドがある場合

コマンドで2)を選択して、python manage.py migrateを実行すると、エラーが生じます。

% python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, books, contenttypes, sessions
Running migrations:
  Applying books.0005_alter_mymodel_first_name...Traceback (most recent call last):
  File "/Users/yoheiwatanabe/.pyenv/versions/3.11.3/lib/python3.11/site-packages/django/db/backends/utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/yoheiwatanabe/.pyenv/versions/3.11.3/lib/python3.11/site-packages/django/db/backends/sqlite3/base.py", line 329, in execute
    return super().execute(query, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
....

  raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/Users/yoheiwatanabe/.pyenv/versions/3.11.3/lib/python3.11/site-packages/django/db/backends/utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/yoheiwatanabe/.pyenv/versions/3.11.3/lib/python3.11/site-packages/django/db/backends/sqlite3/base.py", line 329, in execute
    return super().execute(query, params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django.db.utils.IntegrityError: NOT NULL constraint failed: new__books_mymodel.first_name

この場合、データベースのすべてのレコードに空であるフィールドを手動で追加するか、次のようにファイルを修正してください。

  • 0005_alter_mymodel_first_name.py
# Generated by Django 5.0.4 on 2024-04-30 08:29

from django.db import migrations, models


def provide_default_for_profile(apps, schema_editor):
    MyModel = apps.get_model('books', 'MyModel')
    MyModel.objects.filter(first_name=None).update(first_name='default_value')


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0004_mymodel'),
    ]

    operations = [
        migrations.RunPython(provide_default_for_profile),
        migrations.AlterField(
            model_name='mymodel',
            name='first_name',
            field=models.CharField(max_length=10),
        ),
    ]

このように修正するとpython manage.py migrateを実行してもエラーになりません。

% python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, books, contenttypes, sessions
Running migrations:
  Applying books.0005_alter_mymodel_first_name... OK

2. 新しいフィールドを追加する場合

次のようにprofileフィールドを追加すると、エラーが生じます。

class MyModel(models.Model):
    """My model"""
    first_name = models.CharField(max_length=10)
    last_name = models.CharField(max_length=10)
    profile = models.CharField(max_length=50)
% python manage.py makemigrations
It is impossible to add a non-nullable field 'profile' to mymodel without specifying a default. This is because 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) Quit and manually define a default value in models.py.

null=Trueを追加すると解決されます。またはdefault='hoge'でも問題ありません。

class MyModel(models.Model):
    """My model"""
    first_name = models.CharField(max_length=10)
    last_name = models.CharField(max_length=10)
    profile = models.CharField(max_length=50, null=True)
 % python manage.py makemigrations
Migrations for 'books':
  backend/books/migrations/0006_mymodel_profile.py
    - Add field profile to mymodel
% python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, books, contenttypes, sessions
Running migrations:
  Applying books.0006_mymodel_profile... OK

APIでの確認。"profile": nullのデータを作成する。

3. 新しいフィールドをオプションなしで追加する場合

追加したフィールドprofileにはnull=Trueとなっていますが、それを削除します。

class MyModel(models.Model):
    """My model"""
    first_name = models.CharField(max_length=10)
    last_name = models.CharField(max_length=10)
    profile = models.CharField(max_length=50)
% python manage.py makemigrations
It is impossible to change a nullable field 'profile' on mymodel to non-nullable without providing a default. This is because 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: 2
Migrations for 'books':
  backend/books/migrations/0007_alter_mymodel_profile.py
    - Alter field profile on mymodel
% python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, books, contenttypes, sessions
Running migrations:
  Applying books.0007_alter_mymodel_profile... OK

APIでの確認。"profile": nullでエラーになる。



マイグレーションエラーの解決策が少しわかった気がします。







僕から以上