Djangoのモデルにフィールドを追加する時のフロー

どんなに入念にデータベースを設計しても、機能を追加していく過程でどうしても新しいカラムを追加せざるを得ない場面に遭遇することが起こります。
テーブルごと作り直せれば話は早いのですが、すでにシステムが稼働しているためにそれができない場合、もう少し複雑なことをしなければいけません。
DjangoでModelに新しいFieldを追加した場合に、行うフローを備忘録として記します。

Fieldの変更・追加

myappアプリケーションのMyModelというモデルにnewfieldというカラムを追加する場合。新しく追加するカラムはnull=Trueをセットしておく。これは、カラムを追加する時にnull制約に引っかからないようにするためなので、他にもdefaultを設定するなどの方法で回避してもOK

from django.db import models
class MyModel(models.Model):
    oldfield = models.CharField(max_length=10)
    newfield = models.CharField(max_length=13, null=True)

SQLを確認

manage.pyのsqlコマンドでテーブルのCREATE文を出力する。

$ ./manage.py sql myapp

出力

BEGIN;
CREATE TABLE `myapp_mymodel` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `oldfield` varchar(10) NOT NULL,
    `newfield` varchar(13)
)
;
COMMIT

ALTER TABLEでテーブル構造を変更する

テーブル構造を変更する。MySQLの場合は

/* ADD以下は上で出力したCREATE文の対応する部分をコピペ */
ALTER TABLE `myapp_mymodel` ADD `newfield` varchar(13);

データを挿入

myapp_mymodelテーブルの既存データにnewfieldの値を正しくUPDATEする。

Fieldの制約を正しくセットしなおす

今度は本気の制約を設定する

from django.db import models
class MyModel(models.Model):
    oldfield = models.CharField(max_length=10)
    newfield = models.CharField(max_length=13)

再びCREATE文を出力

$ ./manage.py sql myapp

出力

BEGIN;
CREATE TABLE `myapp_mymodel` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `oldfield` varchar(10) NOT NULL,
    `newfield` varchar(13) NOT NULL
)
;
COMMIT

再びALTER TABLE

/* ADD以下は上で出力したCREATE文の対応する部分をコピペ */
ALTER TABLE `myapp_mymodel` ADD `newfield` varchar(13) NOT NULL;

移行完了

これで完了。データベースにそんなに詳しくないので、あまり正しい方法ではないかも。
その他の注意点

  • 本番環境を変更する前に、開発環境で十分に練習すること
  • できるかぎりバッチ処理の形にして、操作ミスなどが入らないようにする
  • このやり方を行うには、本番環境をメンテナンスのために一時停止する必要がある