Почему миграция Django использует одно и то же случайное значение по умолчанию в каждой строке? - PullRequest
0 голосов
/ 09 октября 2018

Примечание:

Я понимаю и хорошо знаю разницу между передачей функции в качестве параметра и вызовом функции и передачей результата в качестве параметра.Мне кажется, я правильно передаю эту функцию.

Спецификации

  • Django 1.11
  • PostgreSQL 10.4

Сценарий:

У меня в приложении десятки моделей со многими существующими записями.Мне нужно добавить случайное начальное число для каждой из этих моделей, которые будут созданы и установлены при создании нового экземпляра модели.Я также хочу сгенерировать случайное начальное число для существующих экземпляров.

Мое понимание того, как модель Django по умолчанию и Миграции работает, заключается в том, что при добавлении нового поля вмодели, если для этого поля установлено значение по умолчанию, Djano обновит все существующие экземпляры новым полем и соответствующим значением по умолчанию.

Однако, несмотря на то, что я определенно передаю функцию в качестве значения по умолчанию, ифункция генерирует случайное число, Django использует то же случайное число при обновлении существующих строк (например, кажется, что Djano вызывает функцию только один раз, а затем использует возвращаемое значение для всех записей).

Пример

Сокращенная версия моего кода:

def get_random():
    return str(random.random())

class Response(models.Model):
    # various properties
    random = models.CharField(max_length=40, default=get_random)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content = JSONField(null=True)

Случайное поле добавляется после модели, и многие ее экземпляры уже созданы.Появляется команда makemigrations для генерации правильного файла миграции с вызовом migrations.AddField(), передающим в качестве параметра default=get_random.

Однако после запуска makemigrations все существующие существующие экземпляры Responseсодержат точно такое же число в их поле random .Создание и сохранение новых экземпляров модели работают должным образом (с псевдо-уникальным случайным числом).

Обходной путь

Простой обходной путь - просто запустить одноразовый скрипт, который выполняет

for r in Response.objects.all():
    r.random = get_random()
    r.save()

Или переопределите метод save() модели, а затем выполните массовое сохранение.Но я не думаю, что эти обходные пути должны быть необходимыми.Это также означает, что если я хочу создать уникальное поле со случайным значением по умолчанию, мне потребуется несколько миграций.Сначала я должен добавить поле с назначенным значением по умолчанию.Далее мне нужно применить миграцию и вручную повторно инициализировать значения полей.Затем вторая миграция для добавления свойства unique=True.

Кажется, что если Django должен применить значения по умолчанию к существующим экземплярам при makemigrations, то он должен применять их, используя ту же семантику, что и при создании нового экземпляра.Есть ли способ заставить Django вызывать функцию для каждого экземпляра модели при миграции?

1 Ответ

0 голосов
/ 09 октября 2018

Чтобы добавить ненулевой столбец в существующую таблицу, Django должен использовать ALTER TABLE ADD COLUMN ... DEFAULT <default_value>.Это позволяет Django вызывать функцию по умолчанию только один раз, поэтому вы видите, что каждая строка имеет одно и то же значение.

Ваш обходной путь достаточно эффективен, за исключением того, что вы можете заполнить существующие строки уникальными значениями, используя данныемиграция, так что она не требует каких-либо ручных шагов.Вся процедура для этого варианта использования описана в документации: https://docs.djangoproject.com/en/2.1/howto/writing-migrations/#migrations-that-add-unique-fields

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...