2011-01-21 86 views
10

我想知道是否有方法使用動態生成的python屬性使用property()在查詢集上使用Django的filter()。我有每個用戶的first_namelast_name,我想根據它們的連接名稱first_name last_name進行過濾。 (之所以這樣做,是因爲當我自動完成時,我搜索查詢查詢是否匹配名字,姓氏或連接的一部分,例如我想John S匹配John Smith,例如基於動態屬性的Django查詢()

我創建了一個屬性name

def _get_name(self): 
    return self.first_name + " " + self.last_name 
    name = property(_get_name) 

這樣我可以打電話user.name得到級聯名

但是,如果我嘗試做User.objects.filter(name__istartswith=query)我得到的錯誤Cannot resolve keyword 'name' into field.

關於如何做到這一點的任何想法?我是否必須在數據庫中創建另一個字段來存儲全名?

回答

9

filter()在數據庫級別上運行(它實際上是編寫SQL),所以根據您的python代碼(dynamic property in your question)不能用於任何查詢。

這是一個答案從許多其他的答案放在一起在這個部門:)

+0

有趣的是,你不能過濾屬性,但你可以將現有的字段分組並按組進行過濾[下面的答案](http://stackoverflow.com/a/19040011/3999748) – Persijn 2015-04-15 12:18:41

0

認爲這不是在Django可能作爲一個數據庫提交的是不存在的屬性進行過濾,但你可以做什麼,使酷自動完成搜索是這樣的:

if ' ' in query: 
    query = query.split() 
    search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]), 
            User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0]))) 
else: 
    search_results = User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query)) 

此代碼給用戶的系統可以靈活地開始輸入名字或姓氏,並且用戶會感謝你允許這樣做。

+0

想過,但也存在一些問題。有些人的名字或姓氏可能有空格,所以按空格分隔可能會被打破。否則,儘管如此,我已經搜索了名字或姓氏。儘管如此,我建立了一個信號來在創建時填充名稱字段,因此它現在可以工作。謝謝。 – munchybunch 2011-01-21 01:17:18

12

接受的答案是不完全正確的。

對於很多情況下,您可以在模型管理器中將get()覆蓋爲pop動態屬性,然後將要查詢的實際屬性添加到kwargs關鍵字參數字典中。請務必返回super,以便任何常規get()調用返回預期結果。

我只是在粘貼我自己的解決方案,但對於__startswith和其他條件查詢,您可以將一些邏輯添加到split雙下劃線並適當地處理。

這是我的工作,各地通過動態屬性,以允許查詢:

class BorrowerManager(models.Manager): 
    def get(self, *args, **kwargs): 
     full_name = kwargs.pop('full_name', None) 
     # Override #1) Query by dynamic property 'full_name' 
     if full_name: 
      names = full_name_to_dict(full_name) 
      kwargs = dict(kwargs.items() + names.items()) 
     return super(BorrowerManager, self).get(*args, **kwargs) 

在models.py:

class Borrower(models.Model): 
    objects = BorrowerManager() 

    first_name = models.CharField(null=False, max_length=30) 
    middle_name = models.CharField(null=True, max_length=30) 
    last_name = models.CharField(null=False, max_length=30) 
    created = models.DateField(auto_now_add=True) 

在utils.py(爲上下文):

def full_name_to_dict(full_name): 
    ret = dict() 
    values = full_name.split(' ') 
    if len(values) == 1: 
     raise NotImplementedError("Not enough names to unpack from full_name") 
    elif len(values) == 2: 
     ret['first_name'] = values[0] 
     ret['middle_name'] = None 
     ret['last_name'] = values[1] 
     return ret 
    elif len(values) >= 3: 
     ret['first_name'] = values[0] 
     ret['middle_name'] = values[1:len(values)-1] 
     ret['last_name'] = values[len(values)-1] 
     return ret 
    raise NotImplementedError("Error unpacking full_name to first, middle, last names") 
+2

好!順便說一句,你可以使用`isinstance(getattr(self.model,attr),property)`來檢查參數是否屬性。我正在以類似的方式做一些事情...... – 2014-02-03 12:04:17

3

我有一個類似的問題,正在尋找解決方案。理所當然,搜索引擎將是最好的選擇(例如,django-haystackElasticsearch),這就是我將如何實施只用Django的ORM您需要一些代碼(你可以用istartswith替代icontains):

from django.db.models import Value 
from django.db.models.functions import Concat 

queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name') 
return queryset.filter(full_name__icontains=value) 

在我的情況下,我不知道用戶是否會插入'first_namelast_name'或反之亦然,所以我使用了下面的代碼。

from django.db.models import Q, Value 
from django.db.models.functions import Concat 

queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name')) 
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value)) 

使用Django < 1.8,你可能需要求助於extra與SQL CONCAT功能,類似如下:

queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%'])