2016-10-15 10 views
1

adminモデルのget_querysetメソッドをオーバーライドできます。私はこれを使って、OneToOneFieldまたはManyToManyFieldのオブジェクトを選択/プリフェッチします。ただし、モデルのリストビューには簡潔な情報しか表示されず、変更ビューにはさらに多くのオブジェクトが表示されます。これらが表示されない場合は、リストビューでManyToManyField関係をプリフェッチすることは意味がありません。Django Adminのリストビューと変更ビューのクエリーセット最適化の違い

サンプルモデル:

class Location(TimeStampedModel): 
    owner = models.ForeignKey('Profile', on_delete=models.CASCADE) 
    postcode = models.CharField("postcode", max_length=11, blank=True) 
    tenants = models.ManyToManyField('Profile', blank=True) 

サンプル管理モデル:

@admin.register(Location) 
class LocationAdmin(admin.ModelAdmin): 
    list_display = ('owner', 'postcode') 
    fields = ('owner', 'postcode', 'tenants') 
    filter_horizontal = ('tenants',) 

    def get_queryset(self, request): 
     qs = super(LocationAdmin, self).get_queryset(request).select_related('owner__user') 
     qs = qs.prefetch_related('tenants') 
     return qs 

それはクエリセットはモデルのリストビューのために返され、クエリセットが返さごとに異なる最適化を定義することは可能です同じモデルの変更/追加ビュー?

つまり、上記のサンプル管理モデルでは、qs.prefetch_related('tenants')行が変更/追加ビューにのみ関係しますか?

答えて

0

これを達成する最も簡単な方法は、実行中のビューを解決するために使用できるrequest.resolver_match属性を使用することです。以下は、(それは、本質的にいくつかの内部を使用している)脆い/少しハックですが、動作します。また、あなたはこれを必要とするかどうか、それが実際に動作するかどうかを検討すべきである

class LocationAdmin(admin.ModelAdmin): 
    list_display = ['owner', 'postcode'] 
    fields = ['owner', 'postcode', 'tenants'] 
    filter_horizontal = ['tenants'] 

    def get_queryset(self, request): 
     qs = super(LocationAdmin, self).get_queryset(request) 
     qs = qs.select_related('owner__user') 
     if request.resolver_match.func.__name__ == 'change_view': 
      qs = qs.prefetch_related('tenants') 
     return qs 

。変更ビューには主オブジェクトが1つしか表示されないため、オブジェクトリストのN + 1の問題がしばしば適用されません。また、外部キーと多対多フィールドのインラインとウィジェットのクエリは、get_querysetから提供するクエリセットを使用しないことがあります。この場合、Django 1.10.2のテストでは、prefetch_related呼び出しでは、 'change'ビューが実行されたクエリの数は減少しませんでした。 「追加」ビューはget_querysetメソッドをまったく使用しませんでした。

デモアプリを見るhttps://bitbucket.org/spookylukey/djangoadmintips/src/default/queryset_optimization/?at=default

+0

ありがとうございます! 'add'ビューは' get_queryset'を使用しません...私の場合、 'tenants'の水平ウィジェットは、システム内の各' User'(4000ユーザー= 4000追加クエリ)に対して別々のクエリを引き起こしていました。 'prefetch_related'と' select_related'はかなり最適化されていました。それは 'Profile'の' __str__'メソッドが 'User'モデルの情報に依存していたからです。 – interDist

+0

あなたが提案しているこの方法はどのように「ハッキー」ですか?それはDjangoの次のリリースで壊れませんか?一方、私は 'change_view'、' add_view'、 'changelist_view'関数をオーバーライドして、異なる最適化を提供しました。 – interDist

+0

'Profile'の' __str__'メソッドを修正するために、 'prefetch_related'呼び出しをデフォルトのマネージャ' get_queryset'メソッドに追加することもできます。 – spookylukey

関連する問題