Эффективное обновление большого количества записей на основе поля в этой записи с помощью Django

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

update_list = []
qs = Comments.objects.filter(word_count=0)
for comment in qs:
    model_obj = Comments.objects.get(id=comment.id)
    model_obj.word_count = len(model_obj.body.split())
    update_list.append(model_obj)
Comment.objects.bulk_update(update_list, ['word_count'])

Однако в процессе миграции все зависает и, похоже, прерывается. Есть ли у кого-нибудь предложения, как я могу это сделать?

Нелегко определить объем памяти объекта Django, но абсолютный минимум - это объем пространства, необходимый для хранения всех его данных. Я предполагаю, что вам может не хватать памяти, и вы перегружаете страницы.

Вероятно, вы хотите работать партиями, скажем, по 1000 объектов за раз. Используйте Queryset slicing, который возвращает другой queryset. Попробуйте что-нибудь вроде

BATCH_SIZE = 1000 
start = 0
base_qs = Comments.objects.filter(word_count=0)

while True:
    batch_qs = base_qs[ start: start+BATCH_SIZE ]
    start += BATCH_SIZE
    if not batch_qs.exists():
        break

    update_list = []
    for comment in batch_qs:
        model_obj = Comments.objects.get(id=comment.id)
        model_obj.word_count = len(model_obj.body.split())
        update_list.append(model_obj)
    Comment.objects.bulk_update(update_list, ['word_count'])
    print( f'Processed batch starting at {start}' )

Каждый цикл будет освобождать место, занятое предыдущим циклом, когда он заменяет batch_qs и update_list. Оператор print позволит вам наблюдать, как он продвигается с, надеюсь, приемлемой, регулярной скоростью!

Предупреждение - я никогда не пробовал это делать. Мне также интересно, будут ли нарезка и фильтрация хорошо сочетаться друг с другом, или нужно использовать

base_qs = Comments.objects.all()
...
while True:
    batch_qs = base_qs[ start: start+BATCH_SIZE ]
    ....
    for comment in batch_qs.filter(word_count=0) : 

Таким образом, вы прокладываете свой путь через строки во всей таблице DB и извлекаете подмножество каждого фрагмента, которое нуждается в обновлении. Это кажется "более безопасным". Кто-нибудь знает наверняка?

Вернуться на верх