29

Admin を使用してモデルのエントリを一覧表示するたびに、Admin はテーブル内の行をカウントします。さらに悪いことに、クエリをフィルタリングしているときでもそうしているようです。

たとえば、ID が 123、456、789 のモデルのみを表示したい場合は、次のようにします。

/admin/myapp/mymodel/?id__in=123,456,789

しかし、(特に)実行されたクエリは次のとおりです。

SELECT COUNT(*) FROM `myapp_mymodel` WHERE `myapp_mymodel`.`id` IN (123, 456, 789) # okay
SELECT COUNT(*) FROM `myapp_mymodel` # why???

これはmysql + innodbを殺しています。問題はこのチケットで部分的に認識されているようですが、想定されていない場合でもすべての行をカウントするため、私の問題はより具体的なようです。

そのグローバル行数を無効にする方法はありますか?

注: 私は django 1.2.7 を使用しています。

4

7 に答える 7

28

Django 1.8 では、 を設定することでこれを無効にできますshow_full_result_count = False

https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.show_full_result_count

于 2015-01-26T16:23:10.860 に答える
25

わかりました、解決策を見つけたと思います。ピーターが示唆したように、最善のアプローチはプロパティで作業することであり、これは、近似値でカウントを特殊化するカスタム クエリ セット (この投稿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)

次に、管理者で:

class MyAdmin(admin.ModelAdmin):

    def queryset(self, request):
        qs = super(MyAdmin, self).queryset(request)
        return qs._clone(klass=ApproxCountQuerySet)

おおよその関数は、ページ番号 100000 で物事を台無しにする可能性がありますが、私の場合は十分です。

于 2012-05-04T09:32:19.380 に答える
4

これが深刻な問題である場合は、Drastic Actions™ を実行する必要があります。

1.3.1 インストールのコードを見ると、管理コードが によって返されるページネーターを使用していることがわかりget_paginator()ます。デフォルトのページネータ クラスは にあるようですdjango/core/paginator.py。そのクラスには、 (私のコピーの120行目)に_count設定されているというプライベート値があります。Paginator._get_count()これは、と呼ばれる Paginator クラスのプロパティcountを設定するために使用されます。_get_count()それがあなたの目標だと思います。これで舞台は整った。

いくつかのオプションがあります。

  1. ソースを直接変更します。これはお勧めしませが、1.2.7 で行き詰まっているように見えるので、これが最も便利であることに気付くかもしれません。この変更を忘れずに文書化してください。将来のメンテナ (おそらくあなた自身も含む) は、頭を上げてくれてありがとう。

  2. クラスにモンキーパッチを適用します。これは、a) 変更が気に入らなければ、monkeypatch をコメントアウトするだけでよく、b) Django の将来のバージョンで動作する可能性が高いため、直接変更するよりも優れています。_resolve_lookup()テンプレート変数コードのバグが、評価の最上位レベルでは呼び出し可能オブジェクトを認識せず、下位レベルでのみ認識されないというバグがまだ修正されていないため、4 年以上前のモンキーパッチがあります。パッチ (クラスのメソッドをラップする) は 0.97-pre に対して書かれていますが、1.3.1 でも機能します。

_approx_count問題に対してどのような変更を加える必要があるかを正確に把握するのに時間を費やしませんでしたが、適切なクラスclass METAにメンバーを追加してから、その属性が存在するかどうかをテストする方法に沿っている可能性があります。そうである場合はNone、それを実行しsql.count()て設定します。リストの最後のページ (またはその近く) にいる場合は、リセットする必要がある場合もあります。これについてもう少し助けが必要な場合は、私に連絡してください。私のメールは私のプロフィールにあります。

于 2012-05-03T17:04:20.533 に答える
3

admin クラスが使用するデフォルトのページネータを変更することができます。これは、結果を短期間キャッシュするものです: https://gist.github.com/e4c5/6852723

于 2013-10-06T11:20:08.937 に答える