Поле UUID добавлено после данных уже в базе данных. Есть ли способ заполнить поле UUID для существующих данных?

Я добавил поле UUID в некоторые из моих моделей, а затем переместился с Югом. Любые новые объекты, которые я создаю, имеют поле UUID, заполненное правильно. Однако поля UUID во всех моих старых данных равны нулю.

Есть ли способ заполнить данные UUID для существующих данных?

Ответы

Ответ 1

Для следующего образца класса:

from django_extensions.db.fields import UUIDField

def MyClass:
    uuid = UUIDField(editable=False, blank=True)
    name = models.CharField()

Если вы используете Юг, создайте миграцию данных:

python ./manage.py datamigration <appname> --auto

И затем используйте следующий код для обновления миграции с помощью конкретной логики для добавления UUID:

from django_extensions.utils import uuid

def forwards(self, orm):
    for item in orm['mypp.myclass'].objects.all():
        if not item.uuid:
            item.uuid = uuid.uuid4() #creates a random GUID
            item.save()


def backwards(self, orm):
    for item in orm['mypp.myclass'].objects.all():
        if item.uuid:
            item.uuid = None
            item.save()

Вы можете создавать различные типы UUID, каждый из которых генерируется по-разному. модуль uuid.py в Django-extensions содержит полный список типов UUID, которые вы можете создать.

Важно отметить, что если вы запускаете эту миграцию в среде с большим количеством объектов, у нее есть возможность тайм-аут (например, при использовании используемой ткани для развертывания). Альтернативный метод заполнения уже существующих полей будет необходим для производственных сред.

При попытке сделать это с большим количеством объектов возможно исчерпать память (у нас закончилось нехватка памяти и сбой развертывания с 17 000 + объектами).

Чтобы обойти это, вы должны создать пользовательский итератор в своей миграции (или придерживаться его там, где это действительно полезно, и ссылаться на это в вашей миграции). Он будет выглядеть примерно так:

def queryset_iterator(queryset, chunksize=1000):
    import gc
    pk = 0
    last_pk = queryset.order_by('-pk')[0].pk
    queryset=queryset.order_by('pk')
    if queryset.count() < 1
        return []
    while pk < last_pk:
        for row in queryset.filter(pk__gt=pk)[:chunksize]:
            pk = row.pk
            yield row
        gc.collect()

И тогда ваши миграции изменились бы так:

class Migration(DataMigration):

    def forwards(self, orm):
        for item in queryset_iterator(orm['myapp.myclass'].objects.all()):
            if not item.uuid:
                item.uuid = uuid.uuid1()
                item.save()

    def backwards(self, orm):
        for item in queryset_iterator(orm['myapp.myclass'].objects.all()):
            if item.uuid:
                item.uuid = None
                item.save()

Ответ 2

Чтобы добавить значения UUID во все существующие записи, сначала вам нужно убедиться, что ваша модель имеет UUID, зарегистрированный с помощью blank=True, null=True

Затем запустите команду schemamigration с югом и затем откройте полученный файл миграции. Затем отредактируйте файл миграции следующим образом, как показано в этом post

Цитата:

Вам нужно будет импортировать следующий импорт uuid

В конце функции forward() добавьте следующие def forward (self, orm):

...
for a in MyModel.objects.all():
    a.uuid = u'' + str(uuid.uuid1().hex)
    a.save()

Как указано, цикл будет проходить через существующие экземпляры и добавит uuid к нему как часть переноса.