Difficulty using the new DurationField in Django 1.8
I'm having a bit of trouble with Django's new DurationField for models.
I want the user to be able to choose if the duration of an event on my webapp is 1 day, 3 days, or 5 days, with the default choice being 3 days.
At the beginning of my model, I declare the choices:
SHORT = datetime.timedelta(days=1)
MEDIUM = datetime.timedelta(days=3)
LONG = datetime.timedelta(days=5)
DURATION_CHOICES = ((SHORT, '1 day'),(MEDIUM, '3 days'), (LONG, '5 days'),)
Then below, I declare the DurationField:
duration = models.DurationField(choices = DURATION_CHOICES, default = MEDIUM)
I created a ModelForm for the model, and rendered it on the appropriate template. On the form, "3 days" was the preselected choice in the dropdown, and "1 day" and "5 days" are options as well. However, when I submit the form, I get the form validation error "Select a valid choice. 3 days, 0:00:00 is not one of the available choices."
However, when I remove the choices from DurationField and leave the default:
duration = models.DurationField(default = MEDIUM)
I can submit without any issues. What am I doing wrong here?
Did had the same problem the problem is explained in this bugfix ticket
https://code.djangoproject.com/ticket/24897
best way to fix this is to use this custom field while waiting the django team to fix that:
"""
This is a temp DurationField with a bugfix
"""
standard_duration_re = re.compile(
r'^'
r'(?:(?P<days>-?\d+) (days, )?)?'
r'((?:(?P<hours>\d+):)(?=\d+:\d+))?'
r'(?:(?P<minutes>\d+):)?'
r'(?P<seconds>\d+)'
r'(?:\.(?P<microseconds>\d{1,6})\d{0,6})?'
r'$'
)
# Support the sections of ISO 8601 date representation that are accepted by
# timedelta
iso8601_duration_re = re.compile(
r'^P'
r'(?:(?P<days>\d+(.\d+)?)D)?'
r'(?:T'
r'(?:(?P<hours>\d+(.\d+)?)H)?'
r'(?:(?P<minutes>\d+(.\d+)?)M)?'
r'(?:(?P<seconds>\d+(.\d+)?)S)?'
r')?'
r'$'
)
def parse_duration(value):
"""Parses a duration string and returns a datetime.timedelta.
The preferred format for durations in Django is '%d %H:%M:%S.%f'.
Also supports ISO 8601 representation.
"""
match = standard_duration_re.match(value)
if not match:
match = iso8601_duration_re.match(value)
if match:
kw = match.groupdict()
if kw.get('microseconds'):
kw['microseconds'] = kw['microseconds'].ljust(6, '0')
kw = {k: float(v) for k, v in six.iteritems(kw) if v is not None}
return datetime.timedelta(**kw)
class DurationField(CoreDurationField):
def to_python(self, value):
if value is None:
return value
if isinstance(value, datetime.timedelta):
return value
try:
parsed = parse_duration(value)
except ValueError:
pass
else:
if parsed is not None:
return parsed
raise exceptions.ValidationError(
self.error_messages['invalid'],
code='invalid',
params={'value': value},
)