2015-10-07 70 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)