1

製品とカテゴリのテーブルを含む単純なモデルがあります。Product モデルには外部キー Category があります。

カテゴリ/api/vi/categories/のリストを返すtastypie API呼び出しを行うときに、「製品数」/特定のカテゴリを持つ製品の数を決定するフィールドを追加したいと思います。結果は次のようになります。

category_objects[
{ 
   id: 53
   name: Laptops
   product_count: 7
}, 
...
 ]

次のコードは機能していますが、DB へのヒットが重いです

    def dehydrate(self, bundle):
        category = Category.objects.get(pk=bundle.obj.id)
        products = Product.objects.filter(category=category)
        bundle.data['product_count'] = products.count()
        return bundle  

このクエリを作成するより効率的な方法はありますか? おそらく注釈を付けて?

4

2 に答える 2

4

のprefetch_relatedメソッドを使用QuerSetして、select_related を逆にすることができます。

Asper のドキュメント、

prefetch_related(*ルックアップ)

指定された各ルックアップの関連オブジェクトを単一のバッチで自動的に取得する QuerySet を返します。

これは select_related と同様の目的を持ち、どちらも関連オブジェクトへのアクセスによって引き起こされるデータベース クエリの大洪水を防ぐように設計されていますが、戦略はまったく異なります。

脱水機能を次のように変更すると、データベースは一度だけヒットします。

def dehydrate(self, bundle):
    category = Category.objects.prefetch_related("product_set").get(pk=bundle.obj.id)
    bundle.data['product_count'] = category.product_set.count()
    return bundle 

更新 1

脱水関数内でクエリセットを初期化しないでください。queryset は常にMetaクラスでのみ設定する必要があります。django-tastypieドキュメントから次の例を見てください。

class MyResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        excludes = ['email', 'password', 'is_staff', 'is_superuser']

    def dehydrate(self, bundle):
        # If they're requesting their own record, add in their email address.
        if bundle.request.user.pk == bundle.obj.pk:
            # Note that there isn't an ``email`` field on the ``Resource``.
            # By this time, it doesn't matter, as the built data will no
            # longer be checked against the fields on the ``Resource``.
            bundle.data['email'] = bundle.obj.email

        return bundle

機能に関する公式django-tastypie ドキュメントによると、dehydrate()

脱水する

脱水メソッドは、完全に入力された bundle.data を取得し、それに最後の変更を加えます。これは、データの一部が複数のフィールドに依存する可能性がある場合、独自のフィールドを持つ価値のない余分なデータを押し込みたい場合、または返されるデータから何かを動的に削除したい場合に役立ちます。

dehydrate()bundle.data に最後の変更を加えることのみを目的としています。

于 2013-01-12T08:05:11.120 に答える
2

コードは、カテゴリごとに追加のカウント クエリを実行します。annotateこの種の問題に役立つことについては正しいです。

Django は、クエリセットのすべてのフィールドをGROUP BYステートメントに含めます。サーブ制限フィールドが必須フィールドに設定されていることに注意.values()してください。.group_by()

cat_to_prod_count = dict(Product.objects
                                .values('category_id')
                                .order_by()
                                .annotate(product_count=Count('id'))
                                .values_list('category_id', 'product_count'))

上記のdictオブジェクトはマップ [category_id -> product_count] です。

dehydrateメソッドで使用できます。

 bundle.data['product_count'] = cat_to_prod_count[bundle.obj.id]

それでも問題が解決しない場合は、カテゴリ レコードに同様のカウンターを保持し、singals を使用して最新の状態に保つようにしてください。

カテゴリは通常、ツリーのような存在であり、おそらくすべてのサブカテゴリも数えたいと思うでしょう。

その場合、パッケージdjango-mpttを見てください。

于 2013-01-12T08:09:30.230 に答える