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