Эффективное разбиение на страницы и запрос базы данных в 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 в памяти.