Как сериализовать большой набор запросов и записать его в json?

Django 3.2.10, python 3.9

У меня есть QuerySet из 100 000 пользователей. Мне нужно сериализовать их и записать в json файл.

queryset = Users.objects.all()  # 100 000 users

with open('output.json', 'w') as f:
    serializer = MySerializer(queryset, many=True)
    dump(serializer.data, f)

Я пробовал метод .iterator(), но я не понял, как он должен работать. Также я пробовал следующий метод, но он слишком медленный.

queryset = Users.objects.all()  # 100 000 users

with open('output.json', 'w') as f:
    for user in queryset:
        serializer = MySerializer(user)
        dump(serializer.data, f)

Я думаю, что вам не нужно циклически просматривать набор queryset. Просто установите атрибут many=True в Userserializer.

with open('output.json', 'w') as f:
    serializer = MySerializer(queryset, many=True)
    dump(serializer.data, f)

Я нашел этот ответ, который, я думаю, поможет нам: https://stackoverflow.com/a/45143995/202168

Если взять код из этого ответа и применить его к вашей проблеме, то получится что-то вроде:

class StreamArray(list):
    """
    Converts a generator into a list object that can be json serialisable
    while still retaining the iterative nature of a generator.

    i.e. it converts it to a list-like object without having to exhaust the
    generator and keep its contents in memory.
    """
    def __init__(self, generator):
        self.generator = generator
        self._len = 1

    def __iter__(self):
        self._len = 0
        for item in self.generator:
            yield item
            self._len += 1

    def __len__(self):
        """
        Json parser looks for a this method to confirm whether or not it can
        be parsed
        """
        return self._len


def serialized_users(queryset):
    for user in queryset:
        yield MySerializer(user).data


queryset = Users.objects.all()  # 100 000 users

stream_array = StreamArray(serialized_users(queryset))

with open('output.json', 'w') as f:
    for chunk in json.JSONEncoder().iterencode(stream_array):
        f.write(chunk)

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

Возможно, он все еще будет медленным, но он должен избежать нехватки памяти или подобных проблем.

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

Мы можем изменить функцию serialized_users выше для использования пагинации следующим образом:

from django.core.paginator import Paginator

def serialized_users(queryset):
    paginator = Paginator(queryset, 1000)
    for page_number in paginator.page_range:
        page = paginator.page(page_number)
        for user in page.object_list:
            yield MySerializer(user).data

(остальной код как раньше)

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