2010-02-04 98 views
11

如何在Django中創建自定義field lookups在Django中創建自定義字段查找

當過濾查詢集時,django提供了一組您可以使用的查找:__contains,__iexact,__in等等。我希望能夠爲我的經理提供新的查找,因此,例如,有人可能會說:

twentysomethings = Person.objects.filter(age__within5=25) 

,並取回所有Person對象在20到30歲之間的年齡我需要的子類QuerySet還是Manager這個班呢?它將如何實施?

+3

這些答案對他的例子很有用(可能或可能不是他剛纔拋出來表達他的觀點)。但是如果有人會回答實際上被問到的問題,我會喜歡它。 – royal 2010-12-07 03:39:22

+1

@royal我想我的答案涵蓋了它 - 我已經在我正在研究的圖書館中出現了。 – 2011-11-26 22:40:52

回答

1

從Django 1.7開始,有一個簡單的實現方法。你舉的例子其實很相似,從the documentation一個:

from django.db.models import Lookup 

class AbsoluteValueLessThan(Lookup): 
    lookup_name = 'lt' 

    def as_sql(self, qn, connection): 
     lhs, lhs_params = qn.compile(self.lhs.lhs) 
     rhs, rhs_params = self.process_rhs(qn, connection) 
     params = lhs_params + rhs_params + lhs_params + rhs_params 
     return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params 

AbsoluteValue.register_lookup(AbsoluteValueLessThan) 

報名時,你可以使用Field.register_lookup(AbsoluteValueLessThan)代替。

+0

在哪個文件中我應該這樣做? – user1735921 2016-12-16 13:17:49

+0

@ user1735921已經不記得了。在models.py中如何? – d33tah 2016-12-16 13:49:23

+0

嘿,你可以請檢查我的問題: http://stackoverflow.com/questions/41186414/how-to-put-in-string-in-django, 沒有人能夠解決它 – user1735921 2016-12-16 15:19:13

6

而不是創建一個字段查詢,最好的做法是創建一個管理器方法,這可能看起來有點像這樣:

class PersonManger(models.Manager): 
    def in_age_range(self, min, max): 
     return self.filter(age__gte=min, age__lt=max) 

class Person(models.Model): 
    age = #... 

    objects = PersonManager() 

然後使用會像這樣:

twentysomethings = Person.objects.in_age_range(20, 30) 
12

更靈活的方法是編寫自定義QuerySet以及自定義管理器。從ozan的代碼工作:

class PersonQuerySet(models.query.QuerySet): 
    def in_age_range(self, min, max): 
     return self.filter(age__gte=min, age__lt=max) 

class PersonManager(models.Manager): 
    def get_query_set(self): 
     return PersonQuerySet(self.model) 

    def __getattr__(self, name): 
     return getattr(self.get_query_set(), name) 

class Person(models.Model): 
    age = #... 

    objects = PersonManager() 

這可以讓您鏈接自定義查詢。所以,這兩個查詢是有效的:

Person.objects.in_age_range(20,30) 

Person.objects.exclude(somefield = some_value).in_age_range(20, 30) 
+0

非常好!我仍然有點希望有一些API掛鉤查找字段語法,以利用關係遍歷邏輯。我會繼續挖掘。與此同時,我認爲這讓我更接近理解這些片段如何結合在一起。謝謝! – jcdyer 2010-02-06 00:02:14

+0

你可以用[django-qmethod](https://github.com/zacharyvoase/django-qmethod)更容易/乾淨地做到這一點。看起來它有一段時間沒有更新,但我已經使用了很多年,從來沒有遇到任何麻煩。 – Dave 2014-05-02 11:43:51

6

首先,讓我說,有一個地方沒有Django的機器說的意思公開方便你想要什麼。

(編輯 - 事實上,因爲Django的1.7有:https://docs.djangoproject.com/en/1.7/howto/custom-lookups/

也就是說,如果你真的做到這一點,子類QuerySet並重寫_filter_or_exclude()方法。然後創建一個自定義管理器,只返回您的自定義QuerySet(或者Django的Django的QuerySet,yuck)。我們在neo4django中這樣做,在構建Neo4j專用Query對象時重用盡可能多的Django ORM查詢集代碼。

嘗試一些(大致)像這樣,改編自Zach的答案。我已經離開實際的錯誤處理領域查找分析作爲一個練習留給讀者:)

class PersonQuerySet(models.query.QuerySet): 
    def _filter_or_exclude(self, negate, *args, **kwargs): 
     cust_lookups = filter(lambda s: s[0].endswith('__within5'), kwargs.items()) 
     for lookup in cust_lookups: 
      kwargs.pop(lookup[0]) 
      lookup_prefix = lookup[0].rsplit('__',1)[0] 
      kwargs.update({lookup_prefix + '__gte':lookup[1]-5, 
          lookup_prefix + '__lt':lookup[1]+5}) 
     return super(PersonQuerySet, self)._filter_or_exclude(negate, *args, **kwargs) 

class PersonManager(models.Manager): 
    def get_query_set(self): 
     return PersonQuerySet(self.model) 

class Person(models.Model): 
    age = #... 

    objects = PersonManager() 

結束語 - 顯然,如果你想鏈自定義字段查找,這會越晦澀。另外,我通常會在功能上寫得更多一些,並使用itertools來提高性能,但是認爲更清楚地說明一下。玩的開心!

+1

謝謝老兄,這很有幫助。 – nisc 2012-02-03 13:28:25

+0

很高興有人使用它! – 2012-02-03 15:05:20

+1

剛剛找到你的答案!非常感謝。這就是我一直在尋找的。 – jcdyer 2012-04-11 20:28:07

相關問題