如何在Django的管理列表

时间:2017-01-04 15:42:11

标签: python django count admin

为了避免耗时且昂贵的确切数据库计数查询,我想覆盖Django管理类中的count()方法,如下所示:

from django.contrib import admin
from django.db import connection

class CountProxy:
    def __call__(self):
        # how to access the queryset `query` here?
        query = ...

        try:
            if not query.where:
                cursor = connection.cursor()
                cursor.execute("SELECT reltuples FROM pg_class WHERE relname = %s", [query.model._meta.db_table])
                n = int(cursor.fetchone()[0])
                if n >= 1000: return n # exact count for small tables
            return object_list.count()
        except:
            # exception for lists
            return len(object_list)
        return estimated_count

class MyAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyAdmin, self).get_queryset(request)
        qs.count = CountProxy()
        return qs

但我不知道如何访问CountProxy课程中的原始查询集。任何的想法?我知道我可以通过changelist覆盖整个get_changelist视图。但这涉及到Django的回购中的大量重复代码。

3 个答案:

答案 0 :(得分:2)

之前我做了类似的事情,所以我可以提供帮助。

我定义了一个自定义查询集类:

class MyQuerySet(QuerySet):

    def count(self):
        """
        Override count queries (performed by Django ORM) to display approximate value.
        This will speed the admin interface.

        """
        if self._result_cache is not None and not self._iter:
            return len(self._result_cache)

        query = self.query
        if not (query.group_by or query.having or query.distinct):
            cursor = connections[self.db].cursor()
            cursor.execute("SHOW TABLE STATUS LIKE '%s';" % self.model._meta.db_table)
            return cursor.fetchall()[0][4]
        else:
            return self.query.get_count(using=self.db)

然后定义了一个自定义模型管理器:

class MyManager(models.Manager):

    def get_query_set(self):
        return MyQuerySet(self.model)

然后在我的模型中使用它:

class MyModel(models.Model):
    objects = MyManager()

答案 1 :(得分:1)

我可能错了,但您可以将qs作为CountProxy的实例属性传递吗?

class CountProxy:
    def __init__(self, query):
        self.query = query

    def __call__(self):
        # you've already had the query here, do something with self.query

class MyAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyAdmin, self).get_queryset(request)
        qs.count = CountProxy(qs)
        return qs

答案 2 :(得分:0)

这就是Postgres和Django 2.2.x对我有用的

from django.db.models.query import QuerySet
from django.db import connection

class FastCountQuerySet(QuerySet):
    """
    Fast count (estimate) queryset to speedup count
    """
    def count(self):
        """
        Override count queries (performed by Django ORM) to display approximate value.
        This will speed up count i.e. in the admin interface.
        """
        if self._result_cache is not None:
            return len(self._result_cache)

        query = self.query
        if not (query.group_by or query.where or query.distinct):
            # cursor = connections[self.db].cursor()
            cursor = connection.cursor()
            cursor.execute("SELECT reltuples FROM pg_class WHERE relname = %s", [self.query.model._meta.db_table])
            n = int(cursor.fetchone()[0])
            if n >= 1000:
                return n  # exact count for small tables
            else:
                return self.query.get_count(using=self.db)
        else:
            return self.query.get_count(using=self.db)


class CustomManager(models.Manager):
    """
    Custom db manager
    """
    def get_queryset(self):
        return FastCountQuerySet(self.model)

最后覆盖您的模型经理:

class YourModel(models.Model):
    objects = CustomManager()