2

django-mptt に関連して劇的なパフォーマンスの問題に直面しています。これが私のケースです:

  • クイズ教室があります
  • Quizz への FK と Category クラスへの FK を持つ Question クラスがあります。
  • MPTT ツリーである Category クラスがあります (分類が階層的であるため)

現在、7 つの質問を含む実際のクイズと、質問を QuizzAdmin ビューにインラインとして表示する管理ビューがあり、インラインには [選択] フィールドとしてカテゴリが表示されます。

次に、問題が発生します。

  • 質問を prefetch_related としてロードしました(さらに、questions__category をロードしようとしました)。
  • それにもかかわらず、デバッグ ツールバーには、テンプレートのレンダリング時に発生する一連の 16 個のクエリが表示されます (template/edit_inline/tabular.html)。私の開発用ラップトップでは、これをすべてロードするのに 1 分かかります (実際のデータを使用したテスト環境では、10 分を意味します!)

これらの 16 のクエリは、以下の一連のクエリです: (pls は、ダミーのカテゴリでテストしていることに注意してください)

SELECT "quizz_category"."id", "quizz_category"."parent_id", "quizz_category"."name", 
"quizz_category"."name_en", "quizz_category"."name_fr", "quizz_category"."lft",
"quizz_category"."rght", "quizz_category"."tree_id", "quizz_category"."level",
"quizz_category"."description", "quizz_category"."description_en",
"quizz_category"."description_fr" FROM "quizz_category" ORDER BY
"quizz_category"."tree_id" ASC, "quizz_category"."lft" ASC

SELECT "quizz_category"."id", "quizz_category"."parent_id", "quizz_category"."name", 
"quizz_category"."name_en", "quizz_category"."name_fr", "quizz_category"."lft",
"quizz_category"."rght", "quizz_category"."tree_id", "quizz_category"."level",
"quizz_category"."description", "quizz_category"."description_en",
"quizz_category"."description_fr" FROM "quizz_category" WHERE ("quizz_category"."lft" <= 3
AND "quizz_category"."rght" >= 6 AND "quizz_category"."tree_id" = 1 ) ORDER BY
"quizz_category"."lft" ASC

クエリの数を減らすために何ができるか考えていますか?

よろしくお願いします LA

[編集1]

問題の半分を説明する愚かなことがありました: 私のカテゴリの __unicode__() は、オブジェクトの親の __unicode__() を見ていました (幸い、私のツリーは 2 レベルの深さしかありません)

現在、私の最適な構成では、おそらく Select フィールドの選択肢を構築するために、8 つのエントリに対して "SELECT ... FROM quizz_category" (WHERE 句なし) が 9 回あります。

このクエリをキャッシュして一度だけ実行する方法を知っている人はいますか?

注:現在の最適な構成は、 QuestionInline に .select_related('category') を含めることです


class QuestionInline(admin.TabularInline): # admin.StackedInline
    model = Question
    extra = 0
    ordering = ['position',]

    def queryset(self, request):
        return super(QuestionInline, self).queryset(request).select_related('category')


class QuizzAdmin(admin.ModelAdmin):
    list_display = ["name","rating_scale"]
    inlines = [QuestionInline]
    fieldsets = (
        (None, {'fields': (('name'), ('type',), 'description',
                           'rating_scale' )}),
    )

    def queryset(self, request):
        if getattr(self,'is_change_list', False):
            # it's a changelist view, we don't need details on ForeignKey-accessible objects
            return super(QuizzAdmin, self).queryset(request)
        else:
            return super(QuizzAdmin, self).queryset(request).select_related('rating_scale')

    def changelist_view(self, request, extra_context=None):
        self.is_change_list = True
        return super(QuizzAdmin, self).changelist_view(request, extra_context)

class Category(AbstractAnalyticTreeCategory):
    description         = BusinessTextField(_("description"))  # basically a text field of mine

    tree = AnalyticTreeManager()

    def __unicode__(self):
        return self.name

class Quizz(models.Model):
    name                = models.CharField(_("name of the quizz"), unique=True, max_length=60)
    description         = BusinessTextField(_("description"))
    type                = models.CharField(_("type"), choices=QUIZZ_TYPE_CHOICES, default=QUIZZ_SELF_EVALUATION, null=False, blank=False, max_length=2)
    rating_scale        = models.ForeignKey(MCQScale, verbose_name=_("applicable rating scale"), on_delete=models.PROTECT)


    def __unicode__(self):
        return self.name



class Question(models.Model):
    position = models.IntegerField(verbose_name=_("order index"), help_text=_("Order in which the question will appear."))
    quizz               = models.ForeignKey(Quizz, verbose_name=_("Related quizz"), null=False, blank=False, related_name='questions')
    title               = BusinessCharField(_("item"), max_length=60, null=True, blank=True)
    text                = BusinessTextField(_("question text"),)
    category            = TreeForeignKey(Category, verbose_name=_("dimension"), null=True, blank=False, on_delete=models.SET_NULL)

    def __unicode__(self):
        return self.title

これらのクエリについてデバッグ ツールバーに表示される内容は次のとおりです (すべて同じです)。

SELECT "quizz_category"."id", "quizz_category"."parent_id", "quizz_category"."name", "quizz_category"."name_en", "quizz_category"."name_fr", "quizz_category"."lft"," quizz_category"."rght", "quizz_category"."tree_id", "quizz_category"."level", "quizz_category"."description", "quizz_category"."description_en", "quizz_category"."description_fr" FROM "quizz_category" ORDER BY "quizz_category"."tree_id" ASC, "quizz_category"."lft" ASC 3,68816058264% 1,66 Sel Expl 接続: デフォルト 分離レベル: コミットされた読み取り トランザクション ステータス:トランザクションで /Library/Python/2.7/site-packages/django/contrib/staticfiles/handlers.py呼び出し中(72) return self.application(environ, start_response) /Library/Python/2.7/site-packages/django/contrib/admin/widgets.py in render(263) 出力 = [self.widget.render(name, value, *args, **kwargs)] 49

{{ field.contents|改行br }}

50 {% else %} 51
{{ field.field.errors.as_ul }} 52
{{ field.field }} 53
{% endif %} 54
55
{% endfor %} /Library/Python/2.7/site-packages/ジャンゴ/contrib/admin/templates/admin/edit_inline/tabular.html

4

2 に答える 2

1

私はdjangoを使用していますが、DjangoフォームでModelChoiceFieldまたはModelMultipleChoiceFieldのクエリセットの選択肢をキャッシュ1.10するのソリューションはどれもうまくいきませんでした。に基づく別のソリューションの使用を終了しました。私のフォームは 560 の SQL クエリから 10 になりました。私のコード:django-mptttree_item_iterator

from categories.models import Category
from mptt.utils import tree_item_iterator


def get_tree_choices(queryset, level_indicator='+----', ancestors=False):
    choices = []
    for node, tree in tree_item_iterator(queryset, ancestors=ancestors):
        name=''
        if ancestors:
            for i in tree['ancestors']:
                name+=level_indicator
            name+=' %s' % node.name
        else:
            name = str(node)
        choices.append((node.id, name))
    return choices


# Register your models here.
class CompanyAdminForm(forms.ModelForm):
    category=forms.ChoiceField(choices=get_tree_choices(Category.objects.all()))

    error_messages = {
        'invalid_vat_id': _('Invalid Portuguese VAT ID')
    }

    class Meta:
        exclude = ('category',)
        ...

    def save(self, commit=True):
        company=super(CompanyAdminForm, self).save(commit=False)
        company.category=Category.objects.get(id=self.cleaned_data['category'])
        company.save()
        return company
    ...


@admin.register(Company)
class CompanyAdmin(admin.ModelAdmin):
        ...

まだ100%満足というわけではありませんが、少なくとも速いです。

于 2016-10-12T14:28:49.083 に答える
0

だから.. DjangoフォームでModelChoiceFieldまたはModelMultipleChoiceFieldのクエリセットの選択をキャッシュすることに触発された解決策を見つけました。これについては、その投稿で説明しています。

Django admin には、インライン ファクトリ メカニズムが原因で 1 クエリという奇妙なオーバーヘッドがあります (これについては詳しく説明していません)。これは、通常の場合に 2*k + 1 個のクエリがある理由を説明しています (k = インライン フォームセット内のアイテムの数)。

問題が解決したことを願っています。

ライ

于 2013-09-07T21:40:32.807 に答える