2015-05-08 49 views
6

我正試圖在我的數據庫中存儲通常缺少月份和日期的日期。我目前的做法(似乎有這樣做的許多不同的方式)是用於該三個字段:Django中部分日期的日期便利(驗證,顯示等)

dob_year 
dob_month 
dob_day 

我希望獲得儘可能多的DateFields的好處,我可以。我現在能想到的最重要的是驗證。有效期:

2010 
2010,02 
2010,02,28 

無效:

2010,02,30 

還有轉換成整數人類可讀的形式在模板中的問題。這是偉大的,能夠說:

<p>{{my_date|date:"%Y"}}</p> 

但有了這個,我就必須做一些事情很奇怪,因爲我需要它來支持部分日期和定期的。我想我可以用@property方法完成此操作,但我還沒有對此進行整理。

我確定還有其他的便利我也放棄了(比如admin中的日期小工具)。這裏的想法也是受歡迎的,因爲我知道部分日期是一個常見問題,但我現在主要關心驗證。

更新:Twitter上的一位朋友指出,爲此使用三個字段創建了horrible date queries。除非您想考慮如何處理「2011年7月至2012年6月之間」這樣的查詢,否則不要使用三個字段進行部分日期。

+0

爲什麼要使用DateField?這聽起來像你只是想使用Python的'datetime'模塊來驗證一個日期作爲其部分的總和。 – rnevius

+0

非常感謝你的回答。 – danihp

回答

11

將文件的日期作爲文本存儲是一種不好的做法。它不便攜式,而不是django查詢api友好,並且不友好。

即使不是所有日期都需要,您也可以將所有日期存儲在數據庫中。例如,即使您只需要日期,也可以使用Oracle stores date and time in dates

然後您可以使用DateField +「ChoiceField」來存儲partialdate和相關零件信息。例如:2015-02-01在月份級別截斷。 2015-02-28全日制。 2015-01-01在年級截斷。代碼:

class Item(models.Model): 
    PARTIAL_YEAR='%Y' 
    PARTIAL_MONTH='%Y-%m' 
    PARTIAL_DAY='%Y-%m-%d' 
    PARTIAL_CHOICES = (
     (PARTIAL_YEAR, 'Year'), 
     (PARTIAL_MONTH, 'Month'), 
     (PARTIAL_DAY, 'Day'), 
    ) 
    partial_date_date = models.DateField() 
    partial_date_part = models.CharField('Date part', 
              choices=PARTIAL_CHOICES, 
              max_length=10,) 

驗證這兩個領域將由各自己的部件進行驗證。您可以添加表單清理(Cleaning and validating fields that depend on each other)或model clean驗證級別。

Quering容易使使用Q objects空調查詢:

q_is_year = q(partial_date_part = Item.PARTIAL_YEAR) 
q_by_year = q(partial_date_date__year = 2015) 

q_is_month = q(partial_date_part = Item.PARTIAL_MONTH) 
q_by_month = q(partial_date_date__year = 2105) 
q_by_month &= q(partial_date_date__month = 2) 

qs = Item.objects.filter(q_is_year&q_by_year | q_is_month&q_by_month) 

渲染顯示爲了模板呈現:

<p>{{item.partial_date_date|date:item.partial_date_part}}</p> 

渲染形式要渲染表單控件,您可以使用JavaScript更改UI並幫助用戶輸入數據:

date type: (*) Year () Month  () Day 
    date:  [ change form widget dynamically ] 

您可以發送3個窗口小部件控件,以一次形成並顯示一個窗口小部件。更改更改廣播日期類型選擇的可見性。我用MultiWidget

8月13日與EDITED所有示例代碼:

models.py

from django.db import models 
from datetime import date 

class Item(models.Model): 
    PARTIAL_YEAR='%Y' 
    PARTIAL_MONTH='%Y-%m' 
    PARTIAL_DAY='%Y-%m-%d' 
    PARTIAL_CHOICES = (
     (PARTIAL_YEAR, 'Year'), 
     (PARTIAL_MONTH, 'Month'), 
     (PARTIAL_DAY, 'Day'), 
    ) 
    partial_date_part = models.CharField('Date part', 
              choices=PARTIAL_CHOICES, 
              max_length=10,) 
    partial_date_date = models.DateField() 
    some_comment = models.CharField('Comment', max_length=100,) 

    def save(self, *args, **kwargs): 
     if self.partial_date_part==self.PARTIAL_YEAR: 
      self.partial_date_date = date(self.partial_date_date.year, 1, 1) 
     elif self.partial_date_part==self.PARTIAL_MONTH: 
      self.partial_date_date = date(self.partial_date_date.year, 
              self.partial_date_date.month, 1) 
     super(Item, self).save(*args, **kwargs)  

forms.py

from django import forms 
from django.forms import widgets 
from datetime import date 

class DateSelectorWidget(widgets.MultiWidget): 
    def __init__(self, attrs=None): 
     days = [(d, d) for d in range(1,32)] 
     months = [(m, m) for m in range(1,13)] 
     years = [(year, year) for year in (2011, 2012, 2013)] 
     _widgets = (
      widgets.Select(attrs=attrs, choices=days), 
      widgets.Select(attrs=attrs, choices=months), 
      widgets.Select(attrs=attrs, choices=years), 
     ) 
     super(DateSelectorWidget, self).__init__(_widgets, attrs) 

    def decompress(self, value): 
     if value: 
      return [value.day, value.month, value.year] 
     return [None, None, None] 

    def format_output(self, rendered_widgets): 
     return ''.join(rendered_widgets) 

    def value_from_datadict(self, data, files, name): 
     datelist = [ 
      widget.value_from_datadict(data, files, name + '_%s' % i) 
      for i, widget in enumerate(self.widgets)] 
     D = date(
       day=int(datelist[0]),month=int(datelist[1]),year=int(datelist[2]), 
      ) 
     return D 

class ItemForm(forms.Form): 
    partial_date_part = forms.CharField(widget=forms.RadioSelect) 
    partial_date_date = DateSelectorWidget() 

view.py

from django.http import HttpResponseRedirect 
from django.views.generic import View 
from models import Item 
from django.forms.models import modelform_factory 
from .forms import DateSelectorWidget 
from django import forms 
from django.forms import widgets  

class MyDatAppView(View): 
    form_class = modelform_factory(Item , 
            exclude=[], 
            widgets={ 'partial_date_date': 
               DateSelectorWidget() ,}) 
    initial = {'some_comment': '-*-', } 
    template_name = 'form.html' 

    def get(self, request, *args, **kwargs): 
     form = self.form_class(initial=self.initial) 
     return render(request, self.template_name, {'form': form}) 

    def post(self, request, *args, **kwargs): 
     form = self.form_class(request.POST) 
     if form.is_valid(): 
      m=form.save() 
      return HttpResponseRedirect('/') 

     return render(request, self.template_name, {'form': form}) 

選定部分的變化時,應在模板隱藏/顯示日期部分字段添加的JavaScript。

+0

難道你不認爲在CharField中存儲日期使得它比三個單獨的整數字段中的索引更少嗎?例如,如果我想要通過ASC或DESC日期對錶格進行排序不會很棘手? – Charlesthk

+0

@Charlesthk,重讀答案。模型只有2個領域。數據存儲在其中一個。 – danihp

+0

你的解決方案比我的更清潔,我編輯了我的答案;) – Charlesthk