Django:Paginator +原始SQL查询

Sil*_*ght 11 python sql django pagination

我在我的网站上到处都使用Django Paginator,甚至还写了一个特殊的模板标签,以使它更方便.但现在我进入了一个状态,我需要在其中创建一个复杂的自定义原始SQL查询,没有LIMIT将返回大约100K记录.

如何在自定义查询中使用Django Pagintor?

我的问题的简化示例:

我的模特:

class PersonManager(models.Manager):

    def complicated_list(self):

        from django.db import connection

        #Real query is much more complex        
        cursor.execute("""SELECT * FROM `myapp_person`""");  

        result_list = []

        for row in cursor.fetchall():
            result_list.append(row[0]); 

        return result_list


class Person(models.Model):
    name      = models.CharField(max_length=255);
    surname   = models.CharField(max_length=255);     
    age       = models.IntegerField(); 

    objects   = PersonManager();
Run Code Online (Sandbox Code Playgroud)

我在Django ORM中使用pagintation的方式:

all_objects = Person.objects.all();

paginator = Paginator(all_objects, 10);

try:
    page = int(request.GET.get('page', '1'))
except ValueError:
    page = 1

try:
    persons = paginator.page(page)
except (EmptyPage, InvalidPage):
    persons = paginator.page(paginator.num_pages)
Run Code Online (Sandbox Code Playgroud)

这样,Django变得非常聪明,并LIMIT在执行时添加查询.但是当我使用自定义管理器时:

all_objects = Person.objects.complicated_list();
Run Code Online (Sandbox Code Playgroud)

选择所有数据,然后切片python列表,这非常慢.如何使我的自定义管理器的行为类似于内置管理器?

Tom*_*ski 9

特别是看看Paginator的源代码,page()函数,我认为只需要实现切片,并将其转换为SQL查询中的相关LIMIT子句.您可能还需要添加一些缓存,但这看起来像QuerySet,所以也许您可以做其他事情:

  • 您可以使用CREATE VIEW myview AS [您的查询]创建数据库视图;
  • 为该视图添加Django模型,Meta:managed = False
  • 像任何其他模型一样使用该模型,包括切割其查询集 - 这意味着它非常适合与Paginator一起使用

(为了您的信息 - 我已经使用这种方法很长一段时间了,即使与VIEW伪造m2m中间表的复杂的多对多关系.)


Jon*_*ter 6

这是RawPaginator我制作的一个类,它覆盖了Paginator原始查询。它需要一个附加参数,count即查询的总数。它不会对 进行切片,因为您必须通过和object_list在原始查询中分页。OFFSETLIMIT

from django.core.paginator import Paginator

class RawPaginator(Paginator):
    def __init__(self, object_list, per_page, count, **kwargs):
        super().__init__(object_list, per_page, **kwargs)
        self.raw_count = count

    def _get_count(self):
        return self.raw_count
    count = property(_get_count)

    def page(self, number):
        number = self.validate_number(number)
        return self._get_page(self.object_list, number, self)
Run Code Online (Sandbox Code Playgroud)