Добавление логического поля в производственное развертывание 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:

  1. добавлять new_field = models.NullBooleanField(default=False) к вашей модели
  2. Создать схему миграции 1 с makemigrations
  3. Изменить модель, чтобы иметь new_field = models.BooleanField(default=False)
  4. Создать схему миграции 2 с makemigrations
  5. Запустить миграцию схемы 1
  6. Обновить производственный код
  7. Запустите миграцию схемы 2

Если старый рабочий код записывает в таблицу между шагами 5 и 6, нулевое значение new_field будет написано. Между шагами 6 и 7 будет время, когда могут быть нулевые значения для BooleanField, и когда поле будет считано, оно будет нулевым. Если ваш код может справиться с этим, все будет в порядке, а затем шаг 7 преобразует все эти нулевые значения в False. Если ваш новый код не может обработать эти нулевые значения, вы можете выполнить следующие шаги:

  1. добавлять new_field = models.NullBooleanField(default=False) к вашей модели
  2. Создать схему миграции 1 с makemigrations
  3. Запустить миграцию схемы 1
  4. Обновить производственный код
  5. Изменить модель, чтобы иметь new_field = models.BooleanField(default=False)
  6. Создать схему миграции 2 с makemigrations
  7. Запустить миграцию схемы 2
  8. Обновить производственный код

* обратите внимание, что эти методы были протестированы только с 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')
Другие вопросы по тегам