2015-10-07 104 views
0

我正在開發一個小型項目,用戶可以跟蹤他們在合同上工作的時間。每份合同都有明確規定的工作時間,用戶每月可以工作。使用DurationField存儲工作時間

現在出現了幾個問題:如何將此工作時間存儲在我的Django模型中?我最終使用Django 1.8中的DurationField,但是這帶來了它自己的問題,如下所述。我是否應該切換到IntegerField並將工作時間存儲爲分鐘並將其轉換爲模板內的正確格式?然後,我需要在用戶發送表單之後將其重新轉換爲再次以正確的形式存儲。如何和where(models.py,forms.py ..?)我最終會做這兩個轉換?

當使用DurationField我拿出兩個大問題:

  1. 它總是呈現的格式爲「HH:MM:SS」,而我不需要任何秒工作時間的定義。所以我的JavaScript TimePicker不會讓我選擇秒數並將它們歸零。這不是我認爲的最美麗的解決方案。
  2. 當指定工作時間超過24小時(比如80/month)的合約時,Django將DurationField值保存爲「3天,8小時」,但我希望它在我的內部顯示爲「80:00」輸入字段。我知道這是正常的Python timedelta行爲,但有沒有一種方法來定製它?至少只針對前端用戶。

所以我的基本的兩個問題是:我應該堅持DurationField並以某種方式解決我所面臨的問題,或者我應該切換到像IntegerField其他一些領域,做的轉換對我自己,我不知道在哪裏開始。

回答

0

把這個問題擱置了一段時間後,我想出了一個解決這個下面這篇博客後:http://charlesleifer.com/blog/writing-custom-field-django/

到目前爲止代碼工作,因爲我想它做的事。它將工作時間作爲整數存儲在數據庫中,並將其顯示爲HH:MM給用戶。 我仍然不確定自己是否正確使用,或者某些特殊情況下是否缺少某些東西或者可能是錯誤的?我無法圍繞to_python和from_db_value背後的區別開展工作。此外,我從原始代碼中刪除了value_to_string(請參閱博文),因爲它似乎沒有做任何事情。

class WorkingHoursFieldForm(CharField): 
    """ 
    Implementation of a CharField to handle validation of data from WorkingHoursField. 
    """ 
    def __init__(self, *args, **kwargs): 
     kwargs['max_length'] = 5 
     super(WorkingHoursFieldForm, self).__init__(*args, **kwargs) 

    def clean(self, value): 
     value = super(CharField, self).clean(value) 

     # Split submitted duration into list 
     hour_value = value.split('.') 

     # If list does not have two values, let us do some more validation 
     if len(hour_value) != 2: 
      # In this case the user did not supply the form with the correct format. 
      # Therefore we are going to assume that he does not care about 
      # the minutes and we will just append those for him! 
      if len(hour_value)<2: 
       value = hour_value[0] + ".00" 
      # This case should only arise when the format was not correct at all. 
      else: 
       raise ValidationError(_('Working hours entered must be in format HH.MM')) 

     # If the value is in the correct format, check if the total working hours 
     # exceed 80 hours per month (this equals 288.000 seconds) 
     if len(hour_value) == 2: 
      hours, minutes = map(int, value.split('.')) 
      total_seconds = hours*3600 + minutes*60 
      if total_seconds > 80 * 3600: 
       raise ValidationError(_('Contracts may not be longer than 80 hours!')) 

     return value 


class WorkingHoursField(IntegerField): 
    """ 
    Creates a custom field so we can store our working hours in contracts. 
    Working hours are stored as an integer in minutes inside the database. 
    This field accepts input in the format HH.MM and will display it the same way. 
    """ 

    # Get values from database and return them as HH.MM 
    def from_db_value(self, value, expression, connection, context): 
     if value is None: 
     return value 
     hours, minutes = divmod(value, 60) 
     return "%02d.%02d" % (hours, minutes) 

    def to_python(self, value): 
     if value is None: 
      return value 
     if isinstance(value, (int, long)): 
      return value 
     # Split into two values and return the duration in minutes! 
     if isinstance(value, basestring): 
      hours, minutes = map(int, value.split('.')) 
      return (hours * 60) + minutes 
     # I do not know if this is really relevant here? 
     elif not isinstance(value, datetime.timedelta): 
      raise ValidationError('Unable to convert %s to timedelta.' % value) 
     return value 

    def get_db_prep_value(self, value, connection, prepared): 
     return value 

    # This is somehow needed, as otherwise the form will not work correctly! 
    def formfield(self, form_class=WorkingHoursFieldForm, **kwargs): 
     defaults = {'help_text': _('Please specify your working hours in the format HH:MM \ 
          (eg. 12:15 - meaning 12 hours and 15 minutes)')} 
     defaults.update(kwargs) 
     return form_class(**defaults)