Лучшая практика при добавлении нового уникального поля в существующую модель Django - PullRequest
0 голосов
/ 08 июля 2019

У меня есть существующая модель, которая выглядит примерно следующим образом ...

class Resource(models.Model):

    id = models.AutoField(primary_key=True)

Мы использовали это в течение некоторого времени, и теперь у нас есть ~ 1M экземпляров этих Resource объектов (и связанных с нимиForeignKey / else usages) в нашей базе данных.

Теперь мне нужно отследить еще один идентификатор для этой модели, который я хочу использовать уникальным.

other_id = models.IntegerField(unique=True)

This other_idинформация в настоящее время хранится в некоторых внешних CSV-файлах, и я хочу (в какой-то момент процесса) загрузить эту информацию во все существующие Resource экземпляры.

После добавления указанного поля Django makemigrations работаетпросто хорошо.Однако, когда я применяю указанную миграцию к существующей базе данных, я получаю сообщение об ошибке, указывающее, что мне нужно указать значение по умолчанию для использования со всеми существующими экземплярами Resource.Я уверен, что многие из вас видели нечто подобное.

Как лучше всего обойти это ограничение?Некоторые методы, о которых я думал ...

    • Удалите требование unique=True
    • , примените миграцию
    • для внешней загрузки в *Значение 1028 * для всех существующих моделей (с помощью некоторой команды управления или сценария отключения)
    • добавьте обратно unique=True и примените миграцию
    • Сбросить все существующие данные в JSON
    • очистить все таблицы
    • применить миграцию (с уникальным = True)
    • написать скрипт, который загружает данные обратно,добавление правильного other_id значения
  1. (не уверен, если это возможно) - Напишите некоторую настраиваемую логику миграции, чтобы автоматически ссылаться на эти внешние CSV-файлы для загрузки значений other_id при запуске manage.py migrate,Это может вызвать проблемы, если (в какой-то момент в будущем) кто-то повторно запустит эти миграции, и эта часть не будет выполнена (не удается найти существующий ресурс id в CSV для извлечения other_id).

Все это кажется сложным, но опять же, я думаю, то, что я пытаюсь сделать, тоже не самое простое.

Есть идеи?Я должен представить, что кому-то приходилось обходить подобные проблемы в прошлом.

Спасибо!

Ответы [ 2 ]

2 голосов
/ 08 июля 2019

На самом деле, источник или ваша проблема - не само ограничение, а тот факт, что ваше поле не допускает пустых значений и не имеет значения по умолчанию - у вас будет та же самая ошибка с неуникальным полем.

Правильное решение здесь состоит в том, чтобы позволить полю быть нулевым (null=True) и установить по умолчанию значение None (что будет переводиться в sql "null"). Поскольку значения null исключаются из уникальных ограничений (по крайней мере, если ваш поставщик БД соблюдает стандарт SQL), это позволяет применять изменение схемы, в то же время гарантируя, что у вас не будет дубликатов для ненулевых значений.

Тогда вы можете захотеть, чтобы миграция данных загружала известные значения "other_id" и, в конечном итоге, миграцию третьей схемы, чтобы запретить нулевые значения для этого поля - если и только если вы знаете, что вы заполнили это поле для всех записей.

1 голос
/ 08 июля 2019

В Django есть нечто, называемое Миграция данных , где вы создаете файл миграции, который изменяет / удаляет / добавляет данные в вашу базу данных при применении ваших миграций.

В этом случае вы создадите 3различные миграции:

  1. Создайте миграцию, допускающую нулевые значения, с помощью null=True.
  2. Создайте миграцию данных, которая заполняет данные.
  3. Создайте миграцию, которая запрещаетобнулить значения, удалив null=True, добавленные на шаге 1.

После запуска python manage.py migrate все миграции на шаге 1-3 будут применены в правильном порядке.

Ваша миграция данных будет выглядеть примерно так:

from django.db import migrations

def populate_reference(apps, schema_editor):
    MyModel = apps.get_model('yourappname', 'MyModel')
    for obj in MyModel.objects.all():
        obj.other_id = random_id_generator()
        obj.save()

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(populate_reference),
    ]

Вы можете создать пустой файл миграции с помощью команды ./manage.py makemigrations --empty yourappname.

...