Как сериализовать большой набор запросов и записать его в 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
(остальной код как раньше)