2012-04-05 39 views
26

我有一個基本的Django模型,如:Django的Tastypie先進的過濾:如何做複雜的查找符合Q對象

class Business(models.Model): 
    name = models.CharField(max_length=200, unique=True) 
    email = models.EmailField() 
    phone = models.CharField(max_length=40, blank=True, null=True) 
    description = models.TextField(max_length=500) 

我需要像上述模型執行復雜查詢:

qset = (
    Q(name__icontains=query) | 
    Q(description__icontains=query) | 
    Q(email__icontains=query) 
    ) 
results = Business.objects.filter(qset).distinct() 

我曾嘗試以下使用tastypie沒有運氣:

,並在課堂上爲元我tastypie已濾波設置爲:

filtering = { 
     'name: ALL, 
     'description': ALL, 
     'email': ALL, 
     'query': ['icontains',], 
    } 

任何想法我怎麼能解決呢?

感謝 - 牛頓

回答

40

你是在正確的軌道上。但是,build_filters應該將資源查找轉換爲ORM查找。

默認實現將基於__的查詢關鍵字拆分爲key_bits,value對,然後嘗試查找查找到的資源與其ORM等效項之間的映射。

你的代碼不應該應用在那裏只有構建它的過濾器。下面是改進和修正版本:

def build_filters(self, filters=None): 
    if filters is None: 
     filters = {} 
    orm_filters = super(BusinessResource, self).build_filters(filters) 

    if('query' in filters): 
     query = filters['query'] 
     qset = (
       Q(name__icontains=query) | 
       Q(description__icontains=query) | 
       Q(email__icontains=query) 
       ) 
     orm_filters.update({'custom': qset}) 

    return orm_filters 

def apply_filters(self, request, applicable_filters): 
    if 'custom' in applicable_filters: 
     custom = applicable_filters.pop('custom') 
    else: 
     custom = None 

    semi_filtered = super(BusinessResource, self).apply_filters(request, applicable_filters) 

    return semi_filtered.filter(custom) if custom else semi_filtered 

因爲你是用品質的對象,標準apply_filters方法是不是足夠聰明,應用自定義過濾器鍵(因爲是沒有的),但是你可以迅速覆蓋它,添加一個稱爲「自定義」的特殊過濾器。這樣做你的build_filters可以找到一個合適的過濾器,構建它的意思,並將它作爲自定義傳遞給apply_filters,它將直接應用它,而不是試圖從字典中將它的值作爲一個項目解壓縮。

+0

這工作正常。謝謝 – nknganda 2012-04-15 20:53:34

+3

詞典有沒有方法'擴展'。應該是: orm_filters.update({'custom':qset}) – 2012-12-05 22:06:16

+1

此解決方案會導致調用數據庫兩次(對於semi_filtered,然後對於自定義過濾器)。稍微不同的代碼適用於我:如果'custom'在applicable_filters:custom = applicable_filters.pop('custom')中返回Outreaches.objects.filter(custom)else:return super(OutreachResource,self).apply_filters(request,applicable_filters) – 2013-09-23 22:16:54

0

我解決這樣的問題,所以:

Class MyResource(ModelResource): 

    def __init__(self, *args, **kwargs): 
    super(MyResource, self).__init__(*args, **kwargs) 
    self.q_filters = [] 

    def build_filters(self, filters=None): 
    orm_filters = super(MyResource, self).build_filters(filters) 

    q_filter_needed_1 = [] 
    if "what_im_sending_from_client" in filters: 
     if filters["what_im_sending_from_client"] == "my-constraint": 
     q_filter_needed_1.append("something to filter") 

    if q_filter_needed_1: 
     a_new_q_object = Q() 
     for item in q_filter_needed: 
     a_new_q_object = a_new_q_object & Q(filtering_DB_field__icontains=item) 
     self.q_filters.append(a_new_q_object) 

    def apply_filters(self, request, applicable_filters): 
    filtered = super(MyResource, self).apply_filters(request, applicable_filters) 

    if self.q_filters: 
     for qf in self.q_filters: 
     filtered = filtered.filter(qf) 
     self.q_filters = [] 

    return filtered 

這種方法感覺比我見過別人的關注清晰分離。

+0

將特定於請求的信息放在資源實例上是一個非常糟糕的主意。所以'self.q_filters.append(a_new_q_object)'。這是因爲在具有多個線程的部署環境中,最終可能會導致一個請求的狀態影響另一個的狀態。舉例來說,一個請求中構建的所有過濾器實際上可以應用到完全不同的過濾器,具體取決於時間。在這裏看到文檔:http://django-tastypie.readthedocs.io/en/latest/resources.html#why-class-based這是一個傳遞'bundle'對象的問題。 – 2017-12-04 22:28:20

0

在astevanovic的答案中提出了這個想法,並將其清理一下,以下內容應該可以工作,並且更加簡潔。

主要區別在於,使用None作爲關鍵字而不是custom(可能與列名衝突),使apply_filters更加健壯。

def build_filters(self, filters=None): 
    if filters is None: 
     filters = {} 
    orm_filters = super(BusinessResource, self).build_filters(filters) 

    if 'query' in filters: 
     query = filters['query'] 
     qset = (
       Q(name__icontains=query) | 
       Q(description__icontains=query) | 
       Q(email__icontains=query) 
       ) 
     orm_filters.update({None: qset}) # None is used as the key to specify that these are non-keyword filters 

    return orm_filters 

def apply_filters(self, request, applicable_filters): 
    return self.get_object_list(request).filter(*applicable_filters.pop(None, []), **applicable_filters) 
    # Taking the non-keyword filters out of applicable_filters (if any) and applying them as positional arguments to filter()