Nov*_*ova 22 django django-models django-admin
每次我使用Admin列出模型的条目时,Admin都会计算表中的行数.更糟糕的是,即使您过滤查询,它似乎也是如此.
例如,如果我只想显示id为123,456,789的模型,我可以这样做:
/admin/myapp/mymodel/?id__in=123,456,789
Run Code Online (Sandbox Code Playgroud)
但查询运行(以及其他)是:
SELECT COUNT(*) FROM `myapp_mymodel` WHERE `myapp_mymodel`.`id` IN (123, 456, 789) # okay
SELECT COUNT(*) FROM `myapp_mymodel` # why???
Run Code Online (Sandbox Code Playgroud)
哪个杀了mysql + innodb.似乎这个问题在这个故障单中被部分承认,但我的问题似乎更具体,因为它计算所有行,即使它不应该.
有没有办法禁用全局行数?
注意:我使用的是django 1.2.7.
Col*_*son 22
Django 1.8允许你通过设置禁用它show_full_result_count = False.
Nov*_*ova 20
好的,我想我找到了解决方案.正如彼得建议,最好的办法是对工作的count性质,它可以通过自定义查询设置(如图覆盖它做 这个职位),专门用一个近似等价计数:
from django.db import connections, models
from django.db.models.query import QuerySet
class ApproxCountQuerySet(QuerySet):
"""Counting all rows is very expensive on large Innodb tables. This
is a replacement for QuerySet that returns an approximation if count()
is called with no additional constraints. In all other cases it should
behave exactly as QuerySet.
Only works with MySQL. Behaves normally for all other engines.
"""
def count(self):
# Code from django/db/models/query.py
if self._result_cache is not None and not self._iter:
return len(self._result_cache)
is_mysql = 'mysql' in connections[self.db].client.executable_name.lower()
query = self.query
if (is_mysql and not query.where and
query.high_mark is None and
query.low_mark == 0 and
not query.select and
not query.group_by and
not query.having and
not query.distinct):
# If query has no constraints, we would be simply doing
# "SELECT COUNT(*) FROM foo". Monkey patch so the we
# get an approximation instead.
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)
Run Code Online (Sandbox Code Playgroud)
然后在管理员:
class MyAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(MyAdmin, self).queryset(request)
return qs._clone(klass=ApproxCountQuerySet)
Run Code Online (Sandbox Code Playgroud)
近似函数可能会在页码100000上搞乱,但对我的情况来说已经足够了.
我发现Nova的答案非常有用,但我使用了postgres.我稍微修改它以适用于postgres,稍微改变一下来处理表名称空间,稍微不同的"检测postgres"逻辑.
这是pg版本.
class ApproxCountPgQuerySet(models.query.QuerySet):
"""approximate unconstrained count(*) with reltuples from pg_class"""
def count(self):
if self._result_cache is not None and not self._iter:
return len(self._result_cache)
if hasattr(connections[self.db].client.connection, 'pg_version'):
query = self.query
if (not query.where and query.high_mark is None and query.low_mark == 0 and
not query.select and not query.group_by and not query.having and not query.distinct):
# If query has no constraints, we would be simply doing
# "SELECT COUNT(*) FROM foo". Monkey patch so the we get an approximation instead.
parts = [p.strip('"') for p in self.model._meta.db_table.split('.')]
cursor = connections[self.db].cursor()
if len(parts) == 1:
cursor.execute("select reltuples::bigint FROM pg_class WHERE relname = %s", parts)
else:
cursor.execute("select reltuples::bigint FROM pg_class c JOIN pg_namespace n on (c.relnamespace = n.oid) WHERE n.nspname = %s AND c.relname = %s", parts)
return cursor.fetchall()[0][0]
return self.query.get_count(using=self.db)
Run Code Online (Sandbox Code Playgroud)
小智 5
Nova 的解决方案 (ApproxCountQuerySet) 效果很好,但是在较新版本的 Django queryset 方法中被 get_queryset 替换,所以现在应该是:
class MyAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyAdmin, self).get_queryset(request)
return qs._clone(klass=ApproxCountQuerySet)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7334 次 |
| 最近记录: |