2013-01-11 40 views
1

我有一個簡單的模型,其中包括一個產品和類別表。產品型號有一個外鍵類別。Tastypie Dehydrate反向關係計數

當我做一個tastypie API調用,返回一個類別列表/ api/vi/categories/ 我想添加一個字段來確定「產品數量」/具有給定類別的產品數量。其結果會是這樣的:

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 

是否有建立這個查詢更有效的方式?也許帶註釋?

+0

設置緩存? – forivall

回答

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對象是一個map [category_id - > product_count]。

它可以dehydrate方法使用:

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

如果沒有幫助,儘量保持上個類別記錄類似的櫃檯和使用邊界處理以保持最新狀態。

注意類別通常是樹狀生物,您可能還想保留所有子類別的計數。

在這種情況下請看包django-mptt

+0

此解決方案將查詢時間減少了一半。我不得不在我的代碼中添加一個Try Except來處理是否有0 /沒有產品關係 –

3

您可以使用prefetch_related方法QuerSet來反轉select_related。

阿斯皮爾文檔,

prefetch_related(*查找)

返回查詢集,將自動 檢索,在單個批次中,對於每個指定的 查找的相關對象。

這與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

你不應該脫水函數內部初始化查詢集。查詢集應始終設置在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-tastypiedocumentationdehydrate()功能,

脫水

的脫水方法採用現在完全填充bundle.data &使 任何最後改變它。當一段數據 可能依賴於多個字段時,如果您要推入額外的 不值得擁有自己的字段的數據,或者如果您想要動態地將數據從數據中移除回。

dehydrate()僅用於對bundle.data進行最後的修改。

+0

這是不正確的。 '.count()'調用執行單個數據庫查詢。問題是爲每個類別調用「脫水」功能。你的代碼實際上有兩個查詢。一個用於'prefetch_related',另一個用於'.count()'調用,它根本不使用以前的數據庫查詢。相反,它執行單獨的'SELECT COUNT(*)FROM ...'查詢。 –

+0

我已經更新了我的答案,請看看。 – pankaj28843

+0

謝謝 - 在對tastypie文檔進行深入分析後,我應該將queryset設置爲脫水外 –