モデルに簡単な変更を加えてきましたが、次は South を使った、もう少し高度な変更をしてみましょう。
まず初めに、もう少しトリッキーなカラムの型について見てみましょう。 前の章では、テーブルに BooleanField を追加しました - このフィールドはデフォルト値 (False) を持っているため、 既存のレコードを取得した際の該当カラムの値として使われます。 このため、データベースにとっては扱いやすいものとなります。
しかしながら、いくつかのカラムにはデフォルト値が定義されていません。 カラムがヌル許容ならば - つまり null=True ならば - 既存レコードの新しいカラムには NULL が指定されることになります。 もしデフォルト値を指定しない場合には、そのカラムは NOT NULL に (すなわちデフォルトが null=False に) なり、 データベースは、新しいカラムに値を指定することができなくなるため、信頼性のあるカラム追加処理ができません [1] 。
[1] | いくつかのデータベースバックエンドでは、テーブルが空であってもカラムを追加しようとするものがありますが、 このチュートリアルでは遠慮せずに無視させてもらいます。 |
South がこのような状況を見つけると、ポップアップを出して、どのように処理するかを問い合わせてきます; この動作を見てみましょう。
まず、デフォルト値を持たずに、ヌル許容でもないフィールドを新しく追加して、モデルを変更しましょう:
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
では次のように、この変更のマイグレーションを South に自動生成させましょう:
./manage.py schemamigration southtut --auto
? The field 'Knight.shrubberies' does not have a default specified, yet is NOT NULL.
? Since you are adding or removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice:
South は二つの選択肢を提供してきます; 1番目を選択すると、コマンドは何もせずに終了するので、 models.py を編集して、新しく追加したフィールドにデフォルト値を設定しましょう。
2番目を選択した場合、このマイグレーションに適用するデフォルト値を入力するための Python プロンプトが表示されます。 入力したデフォルト値は、現在の既存レコードだけに使用されます - これは、そのモデルフィールドにデフォルト値を持たせたくない場合に有用な選択肢です。
ここでは2番目を選択して、デフォルト値に 0 を入力してみましょう (IntegerField ですからね)
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 0
+ Added field shrubberies on southtut.Knight
Created 0003_auto__add_field_knight_shrubberies.py. You can now apply this migration with: ./manage.py migrate southtut
生成されたマイグレーションを見ると、新しいフィールドにデフォルト値が指定されていることがわかります。 これでデータベースが困ることはないでしょう。最後にマイグレーションを実行ます:
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0003_auto__add_field_knight_shrubberies.
> southtut:0003_auto__add_field_knight_shrubberies
- Loading initial data for southtut.
South は新しいフィールド (または削除したフィールド) だけでなく、 unique 属性の変更を含めた、ほとんどのフィールド変更を発見することができます。
それでは Knight モデルにユニークな名前を持たせましょう:
from django.db import models
class Knight(models.Model):
name = models.CharField(max_length=100, unique=True)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
自動マイグレーション生成を実行します:
$ ./manage.py schemamigration --auto southtut
+ Added unique constraint for ['name'] on southtut.Knight
Created 0004_auto__add_unique_knight_name.py. You can now apply this migration with: ./manage.py migrate southtut
ご欄のように unique の変更を発見しました; このマイグレーションを適用しましょう:
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0004_auto__add_unique_knight_name.
> southtut:0004_auto__add_unique_knight_name
- Loading initial data for southtut.
同様に South はモデルの Meta に指定された unique_together の変更も発見できます。
South はまた自動的に ManyToMany フィールドを見つけます; フィールドを追加した時に、South は ManyToMany で表されるテーブルを生成し、 フィールドを削除した時にはそのテーブルを削除します。
ただひとつの例外は ‘through モデル’ を持っていた場合 (through= オプションを指定した場合) です - モデルが検知された時点で、そのモデルのテーブルが既に作成されているため、 South はその ManyToMany フィールドに対して何の処理もしません。
マイグレーションファイルをよく見てみると、South はクラスにフィールドの定義と、 フィールドのコンストラクタに渡す引数を保存していることがわかります。
Python には、クラスのコンストラクタで使用される引数を直接取得する方法が無いため、 South は モデルイントロスペクション を使って、フィールドにどんな引数が渡されたのかを導き出します。 これによって、渡された引数がフィールド内のどの変数に保持されているのかを知ることができ、 この情報を使って引数を直接再構築できるのです。
カスタムフィールド (自分で書いたものでも、サードパーティ製アプリに含まれるものでも) はそれぞれ種々様々なため、 South は追加ヘルプテキスト以外の引数を、どのように取得するか解決することができません。 このためカスタムフィールドを追加、変更、削除した場合、 South は処理を諦め、各カスタムフィールドのルールを渡すように表示します; このトピックについては カスタムフィールド において詳しくカバーします。
South はモデルに対する、日々の操作のほとんどをサポートします; もし興味があるならば 自動検出をサポートしているアクション一覧 を参照して下さい。
さて次は Part 3: 高度なコマンドとデータマイグレーション に進みましょう。