Теперь, когда мы знаем как обслуживать простые изменения модели, давайте посмотрим на более сложные случаи, которые Вы так же можете обработать при помощи South.
Значение по умолчанию
Во-первых, давайте займёмся более хитрыми типами колонок. В прошлой части мы добавили BooleanField в таблицу — такую операцию БД легко обработать, так как у него есть значение по умолчанию (False). Это значение и будет установлено для всех уже имеющихся записей в БД.
Однако для некоторых колонок нет предопределённого значения по умолчанию. Если в колонке может присутствовать значение null, то есть null = True, тогда уже существующие записи получат значение null. В противном же случае, если колонка NOT NULL (null = False, свойство колонки по умолчанию), то в таком случае нет значения, которое БД могла бы присвоить уже имеющимся записям, так что Вы не сможете просто добавить колонку (однако, некоторые БД могут позволить Вам добавить колонку в пустую таблицу).
Если South обнаружит такую ситуацию, он тут же спросит у Вас что делать. Давайте посмотрим на это.
Для начала изменим нашу модель добавив поле, которое не имеет значения по умолчанию и не может быть равно null:
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)
Теперь давайте попробуем сделать миграцию на эту модель:
./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 (так как это, в конце концов, числовое поле):
? 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.
Во-первых, давайте изменим схему нашего Knigts, чтобы он имел уникальные поля:
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
Как Вы можете видеть, он определил появление нового ограничения на поле name. Вы можете теперь применить его:
$ ./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 может обнаружить появление или изменение unique_together в подклассе Meta вашей модели.
Поля ManyToMany
South должен автоматически определять поля ManyToMany. Когда Вы добавляете такое поле, South создаёт таблицу, представляющую это отношение, а когда удаляете — South её удаляет.
Единственное исключение — through model, т. е. если Вы используете опцию through. Так как таблица для этой модели уже создана, South не будет делать ничего с этими полями ManyToMany.
Пользовательские поля
Если Вы внимательнее посмотрите на файлы миграции, Вы увидите, что South хранит определения полей сохраняя их класс и аргументы, необходимые для передачи в конструктор поля.
Так как в Python нет способа получить аргументы, используемые в классе конструктора, напрямую, South использует нечто под названием «model introspector» для того, чтобы выяснить, какие аргументы были переданы в конструктор поля. Мы узнаем, что хранится в переменных, которым присваивается значение аргументов и таким образом можем реконструировать сами переданные значения.
Поскольку пользовательские поля (написанные Вами или полученные от сторонних приложений) совсем другие, South не может получить значения для их аргументов без дополнительной помощи. Так что если Вы хотите добавить / изменить / удалить пользовательское поле, South понадобится ваша помощь и он попросит Вас задать правила для этих полей. Эта тема будет затронута в разделе «пользовательские поля».
Ещё?
South поддерживает большинство операций, с которыми Вы будете встречаться каждый день. Если Вам интересно — то есть полный список автоопределяемых изменений.
Теперь, скорее всего, Вам захочется прочитать третью часть руководства.
Автор: Ishayahu Lastov