django中的高效分页和数据库查询

时间:2013-04-23 05:36:23

标签: python django pagination querying

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,Paginator根据用户所在的页面从html GET中对其进行分块。分页符是否以某种方式使这个可以接受,或者这完全是内存效率低下的?如果效率低下,怎么改进?

另外,一个相关的主题。如果有人这样做:

   Model.objects.all()[:40]

这段代码是否意味着所有模型都被推入内存,我们拼出了40个?这很糟糕。或者这是否意味着我们只查询并将40个对象推入内存期?

感谢您的帮助!

3 个答案:

答案 0 :(得分:18)

mymodel.objects.all()生成一个查询集,而不是列表。查询集是懒惰的 - 在您实际尝试使用它们之前,不会发出任何请求,也不会执行任何操作。切片查询集也不会在内存中加载整个该死的东西只是为了得到一个子集,但在点击数据库之前会在SQL查询中添加限制和偏移量。

答案 1 :(得分:0)

使用paginator时没有任何内存效率低下。查询集被懒惰地评估。在您的通话Paginator(models, 7)中,models是一个截至目前尚未评估的查询集。所以,直到现在数据库还没有被击中。此时,内存中也没有包含所有模型实例的列表。

如果要获取页面,即paginatedPage = paginator.page(pageNumber),则在此查询集上进行切片,此时仅数据库被命中,数据库返回包含模型实例的查询集。然后切片只返回页面上应该存在的对象。因此,只有切片的对象才会进入内存中的列表。在一个页面上说你要显示10个对象,只有这10个对象将保留在内存中。

有人的时候;

Model.objects.all()[:40]

切片列表时,会创建一个新列表。在您的情况下,将创建仅包含40个元素的列表,并将其存储在内存中的某个位置。没有其他列表,因此不会有任何列表包含内存中Model的所有实例。

答案 2 :(得分:0)

使用上面的信息我想出了一个视图函数装饰器。 json_list_objects将djanog对象传递给django对象的已知关系字段的json-ready python dicts,并将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