Добавление логического поля в производственное развертывание Django
У меня есть производственное развертывание Django (Django 1.11) с базой данных PostgreSQL. Я хотел бы добавить логическое поле со значением по умолчанию для одной из моих моделей:
class MyModel(models.Model):
new_field = models.BooleanField(default=False)
Для развертывания мне нужно либо обновить код на серверах, либо сначала выполнить миграцию, но, поскольку это производственное развертывание, запросы могут (и будут) выполняться между моим обновлением базы данных и обновлением сервера. Если я сначала обновлю сервер, я получу OperationalError no such column
Поэтому мне необходимо сначала обновить базу данных.
Однако, когда я сначала обновляю базу данных, я получаю следующую ошибку из запросов, сделанных на сервере, прежде чем она будет обновлена новым кодом:
django.db.utils.IntegrityError: Сбой ограничения NOT NULL: myapp_mymodel.new_field
На первый взгляд, это не имеет смысла, потому что поле имеет значение по умолчанию. Углубляясь в это дальше, кажется, что значения по умолчанию обеспечиваются логикой Джанго. Если на сервере нет обновленного кода, он не будет передавать столбец в SQL для обновления, которое SQL интерпретирует как NULL.
Учитывая это, как мне развернуть это новое логическое поле в моем приложении, чтобы мои пользователи не получали никаких ошибок?
1 ответ
Миграции всегда следует запускать в начале развертываний, иначе у вас возникнут другие проблемы. Решением этой проблемы является разделение изменений на два развертывания.
В развертывании 1 поле должно быть обнуляемым (либо NullBooleanField
или же null=True
). Вы должны выполнить миграцию для кода в этом состоянии и убедиться, что остальная часть вашего кода не потерпит крах, если значение поля равно None
, Это необходимо, поскольку запросы могут отправляться на серверы, у которых еще нет нового кода; если эти серверы создают экземпляры модели, они будут создавать ее с нулевым полем.
В развертывании 2 вы устанавливаете поле как необнуляемое, выполняете миграцию для этого и удаляете любой дополнительный код, написанный для обработки случаев, когда значение поля равно None
, Если для поля не задано значение по умолчанию, миграция, которую вы выполняете для этого второго развертывания, должна будет заполнить значения для объектов, которые имеют None
в этом поле.
Техника двух развертываний также необходима для безопасного удаления полей, хотя выглядит немного иначе. В первом развертывании вы удаляете поле из файла моделей и все ссылки на него из своего кода, но не развертываете миграцию по умолчанию для удаления поля. Вместо этого в развертывании 1 есть пользовательская миграция, в которой для поля устанавливается значение NULL. Затем в развертывании 2 вы развертываете миграцию, чтобы фактически удалить поле из базы данных.
Вы можете сделать это, начав с NullBooleanField
:
- добавлять
new_field = models.NullBooleanField(default=False)
к вашей модели - Создать схему миграции 1 с makemigrations
- Изменить модель, чтобы иметь
new_field = models.BooleanField(default=False)
- Создать схему миграции 2 с makemigrations
- Запустить миграцию схемы 1
- Обновить производственный код
- Запустите миграцию схемы 2
Если старый рабочий код записывает в таблицу между шагами 5 и 6, нулевое значение new_field
будет написано. Между шагами 6 и 7 будет время, когда могут быть нулевые значения для BooleanField, и когда поле будет считано, оно будет нулевым. Если ваш код может справиться с этим, все будет в порядке, а затем шаг 7 преобразует все эти нулевые значения в False. Если ваш новый код не может обработать эти нулевые значения, вы можете выполнить следующие шаги:
- добавлять
new_field = models.NullBooleanField(default=False)
к вашей модели - Создать схему миграции 1 с makemigrations
- Запустить миграцию схемы 1
- Обновить производственный код
- Изменить модель, чтобы иметь
new_field = models.BooleanField(default=False)
- Создать схему миграции 2 с makemigrations
- Запустить миграцию схемы 2
- Обновить производственный код
* обратите внимание, что эти методы были протестированы только с Postgres.
Как правило, процесс обновления django выглядит следующим образом:
МЕСТНОЕ РАЗВИТИЕ ENV:
- Измените свою модель локально
- Перенос модели (makemigrations python manage.py)
- Проверьте свои изменения локально
- Зафиксируйте и отправьте ваши изменения на (git) сервер
ПО ПРОИЗВОДСТВЕННОМУ СЕРВЕРУ:
- Установить параметры ENV
- Извлечь из вашей системы контроля версий (git fetch --all; git reset --hard origin/master)
- обновить зависимости python (например, pip install -r needs.txt)
- мигрировать (manage.py migrate_schemas)
- обновить статические файлы (python manage.py collectstatic)
- перезапустите сервер django (зависит от сервера, но может быть что-то вроде 'python manage.py runserver')