2016-03-24 69 views
0

我爲我的前端使用DataTables,它允許全局搜索(遍歷所有字段以查找匹配項)。如何使用字符串在Django中過濾DateField或DateTimeField?

這就是我希望能夠在Django做(1.9.1版本):

  • 使用「馬」作爲一個的DateField過濾器,使所有三月和五月日期 返回

  • 使用「3月1日」作爲一個的DateField過濾器,使所有日期三月間 10 - 3月19日返回

一些修修補補之後,我有這樣的自定義查找

from django.db.models import Lookup 
from django.db.models.fields import DateField 

@DateField.register_lookup 
class DateTextFilter(Lookup): 
    lookup_name = 'dttxt' 
    def as_postgresql(self, compiler, connection): 
     lhs, lhs_params = self.process_lhs(compiler, connection) 
     rhs = self.rhs.strftime("%b %d, %Y") 
     return "to_char(%s,'Mon DD, YYYY') ~* '%s'", ([lhs,rhs]) 

我像這樣運行:

outputQ = Q(**{"appt_date"+"__dttxt" : "Mar 09"}) 
Appts.objects.filter(outputQ) 

它生產運行(從Postgres),SQL,但是當我運行過濾器( ),我得到錯誤

ValidationError: [u"'Mar 09' value has an invalid date format. 
It must be in YYYY-MM-DD format."] 

夠公平的,「Mar 09」是不是一個有效的日期 - 但是原始的SQL是有效的,並運行(我測試了這個以「2016年3月9日」繞過這個錯誤並獲得Django的創建SQL)

SELECT "appts"."appt_id", "appts"."client_id", "appts"."therapist_id", 
"appts"."agency_id", "appts"."appt_date", "appts"."appt_start", 
"appts"."appt_end", "appts"."cpt_code", "appts"."client_owe", 
"appts"."insur1_owe", "appts"."insur2_owe", "appts"."note", 
"appts"."note_flag", "appts"."ct_fee_pd", "appts"."subm_date", 
"appts"."updated" FROM "appts" WHERE to_char("appts"."appt_date",'Mon DD, 
YYYY') ~* 'Mar 09' 

我可以使用extra()但這將被棄用。

我可能要結束使用這個SO algorithm解析字符串(使用原始sql太大了妥協只是爲了獲得此功能),但我會失去一些我想要的功能。

不知道這是否可行...

回答

0

溶液#1

創建構建Q對象以測試部件的時間的方法:如果該字段是將DateField,該方法被稱爲像這樣

def _filter_by_date_field(self, fld_value, searchableColumn, outputQ): 
    mon_val = -1 
    Mons = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'] 
    if len(fld_value) >= 3 and fld_value[0:3].lower() in Mons: 
     mon_val = next(i for i,v in enumerate(Mons) \ 
        if v == fld_value[0:3].lower())+1 
     outputQ |= Q(**{searchableColumn+"__month" : mon_val}) 
    if len(fld_value) >= 6 and mon_val > -1 and \ 
       re.search(r'^\d{2}$', fld_value[4:6]): 
     outputQ &= Q(**{searchableColumn+"__day" : fld_value[4:6]}) 
    if len(fld_value) >= 8 and mon_val > -1 and \ 
       re.search(r'^\d{4}$', fld_value[8:12]): 
     outputQ &= Q(**{searchableColumn+"__year" : fld_value[8:12]}) 
    return outputQ 

if isinstance(<ModelName>.model._meta.get_field(searchableColumn), DateField): 
     outputQ |= self._filter_by_date_field(customSearch,\ 
               searchableColumn,outputQ) 

以下是SQL Django構建完整日期的示例:

EXTRACT('month' FROM "get_appts_vw"."appt_date") = 3) AND 
    EXTRACT('day' FROM "get_appts_vw"."appt_date") = 5 AND 
    "get_appts_vw"."appt_date" BETWEEN '2016-01-01'::date AND '2016-12-31'::date) 

解決方案#2

創建使用CharField(其中原始模型使用的DateField)

class ApptView(models.Model): 
    appt_date = models.CharField(max_length=12) 
      …. 
class Meta: 
    db_table = u'get_appts_vw' # view name 

認爲

CREATE VIEW get_appts_vw AS 
    select to_char(a.appt_date,'Mon DD, YYYY') as appt_date 
    …. 
    from appts 

這工作,但模型的數據庫視圖仍然requires an extra()子句做分類

解決方案#3

(我用這個)

from django.db.models import Lookup 
from django.db.models.fields import DateField 
from django.db.models import CharField, Transform 

@DateField.register_lookup 
class TextDate(Transform): 
    lookup_name = 'txtdt' 

    @property 
    def output_field(self): 
     return CharField() 


@TextDate.register_lookup 
class DateTextFilter(Lookup): 
    lookup_name = 'dttxt' 

    def as_postgresql(self, compiler, connection): 
     lhs, lhs_params = compiler.compile(self.lhs.lhs) 
     rhs, rhs_params = self.process_rhs(compiler, connection) 
     params = lhs_params + rhs_params 
     return 'to_char(%s,\'Mon DD, YYYY\') ~* %s ' % (lhs, rhs), params 

這樣調用

from .CustomFilters import * # or whatever you name your custom filters module 
.... 
    if isinstance(<ModelName>.model._meta.get_field(searchableColumn), DateField): 
      outputQ |= Q(**{<column_name>+"__txtdt__dttxt" : <search_value>}) 
+0

你的最後一個看起來很奇怪。我期望'TextDate'變換中的'to_char(...)'調用,然後查詢變爲'column__txtdt__like = value' –

+0

@ShaiBerger,對您的解決方案感興趣。我見過使用簡單函數(ABS,lower等)的變換,但我還沒有看到變換就像我需要的情況,即to_char(%s,\'Mon DD,YYYY \')。如果你有一個例子,我會很有興趣看到你似乎已經寫好了 – MIkee

+0

...只是試試它。隨意詢問您的解決方案是否無效。在一個函數與一個參數之間沒有什麼區別(對於這個問題),而有兩個參數的函數是一個常量。 –

相關問題