Эффективное разбиение на страницы и запрос базы данных в django
Были некоторые примеры кода для django pagination, которые я использовал некоторое время назад. Возможно, я ошибаюсь, но, просматривая код, похоже, что он уничтожает тонны памяти. Я искал лучшее решение, вот код:
# in views.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
...
...
def someView():
models = Model.objects.order_by('-timestamp')
paginator = Paginator(models, 7)
pageNumber = request.GET.get('page')
try:
paginatedPage = paginator.page(pageNumber)
except PageNotAnInteger:
pageNumber = 1
except EmptyPage:
pageNumber = paginator.num_pages
models = paginator.page(pageNumber)
return render_to_resp ( ..... models ....)
Я не уверен в тонкости этого кода, но из того, как он выглядит, первая строка кода извлекает каждую модель из базы данных и вставляет ее. Затем он передается в Paginator, который разбивает его на основе того, на какой странице пользователь находится из html GET. Является ли paginator каким-то образом это приемлемым, или это полностью неэффективно? Если это неэффективно, как его можно улучшить?
Также, связанная тема. Если кто-то делает:
Model.objects.all()[:40]
Означает ли этот код, что все модели вставляются в память, и мы собиваем 40 из них? Что плохо. Или это означает, что мы запрашиваем и нажимаем только 40 объектов в период памяти?
Благодарим за помощь!
Ответы
Ответ 1
mymodel.objects.all()
выводит запрос, а не список. Querysets являются ленивыми - запрос не выдается, и ничего не делается, пока вы на самом деле не попытаетесь их использовать. Также нарезка набора запросов не загружает всю проклятую вещь в памяти только для того, чтобы получить подмножество, но добавляет ограничение и смещение к SQL-запросу перед удалением базы данных.
Ответ 2
Используя приведенную выше информацию, я придумал декоратор функции вида. Json_list_objects переносит объекты djanog в json-ready python dicts из известных полей отношений объектов django и возвращает jsonified list как {count: results:}.
Другие могут оказаться полезными.
def with_paging(fn):
"""
Decorator providing paging behavior. It is for decorating a function that
takes a request and other arguments and returns the appropriate query
doing select and filter operations. The decorator adds paging by examining
the QueryParams of the request for page_size (default 2000) and
page_num (default 0). The query supplied is used to return the appropriate
slice.
"""
@wraps(fn)
def inner(request, *args, **kwargs):
page_size = int(request.GET.get('page_size', 2000))
page_num = int(request.GET.get('page_num', 0))
query = fn(request, *args, **kwargs)
start = page_num * page_size
end = start + page_size
data = query[start:end]
total_size = query.count()
return json_list_objects(data, overall_count=total_size)
return inner
Ответ 3
При использовании paginator нет ничего неэффективного. Querysets оцениваются лениво. В вашем вызове Paginator(models, 7)
, models
- это набор запросов, который не был оценен до этой точки. Итак, до сих пор база данных не пострадала. Кроме того, в данный момент в памяти нет списка, содержащего все экземпляры модели.
Когда вы хотите получить страницу i.e at paginatedPage = paginator.page(pageNumber)
, на этом запросе выполняется нарезка, только в этот момент база данных попадает, и база данных возвращает вам набор запросов, содержащий экземпляры модели. А затем нарезка возвращает только объекты, которые должны быть на странице. Таким образом, только фрагментированные объекты войдут в список, который будет находиться в памяти. Скажите на одной странице, что вы хотите показать 10 объектов, только эти 10 объектов останутся в памяти.
Когда кто-то делает;
Model.objects.all()[:40]
Когда вы нарезаете список, создается новый список. В вашем случае список будет создан только с 40 элементами и будет храниться где-то в памяти. Никакого другого списка не будет и поэтому не будет списка, который содержит все экземпляры Model
в памяти.