Эффективное разбиение на страницы и запрос базы данных в django
были некоторые примеры кода для разбиения на страницы django, которые я использовал некоторое время назад. Возможно, я ошибаюсь, но при просмотре кода кажется, что он тратит тонны памяти. Я искал лучшее решение, вот код:
# 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 объектов в период памяти?
Спасибо за вашу помощь!
3 ответов
mymodel.objects.all()
дает queryset, а не список. Запросы ленивы - запрос не выдается и ничего не делается, пока вы не попытаетесь их использовать. Также нарезка набора запросов не загружает всю чертову вещь в память только для получения подмножества, но добавляет ограничение и смещение к SQL-запросу перед попаданием в базу данных.
используя приведенную выше информацию, я придумал декоратор функции просмотра. Json_list_objects принимает объекты djanog в JSON-готовые Python-дикты известных полей отношений объектов django и возвращает список jsonified как {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
нет ничего неэффективного в памяти при использовании paginator. Запросы оцениваются лениво. В вашем звонке Paginator(models, 7)
, models
является queryset, который не был оценен до этого момента. Так что до сих пор база данных не пострадала. Также на данный момент в памяти нет списка, содержащего все экземпляры модели.
когда вы хотите получить страницу i.e at paginatedPage = paginator.page(pageNumber)
, нарезка выполняется на этом наборе запросов, только в этот момент база данных попадает и база данных возвращает вам набор запросов, содержащий экземпляры модели. А затем нарезка возвращает только те объекты, которые должны быть на странице. Таким образом, только нарезанные объекты будут входить в список, который будет там в памяти. Скажем, на одной странице нужно показать 10 объектов, только эти 10 объектов в памяти.
когда кто-то делает;
Model.objects.all()[:40]
при нарезке списка создается новый список. В вашем случае список будет создан только с 40 элементами и будет храниться где-то в памяти. Никакой другой список не будет там и так не будет списка, который содержит все экземпляры Model
в памяти.