Durationfield django
Asked Answered
M

2

8

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?

Misprint answered 2/4, 2015 at 17:56 Comment(0)
A
2

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},
            )
Adria answered 2/6, 2015 at 10:52 Comment(1)
looks like that ticket was addressed in 1.8.3 docs.djangoproject.com/en/1.8/releases/1.8.3Earthquake
Z
0

Somehow model choices option not given timedelta string when render or convert into template.

And the Model DurationField Field DurationField Not working...

I solved it by using ChoiceField

Code:

class TestForm(ModelForm):
    SHORT_1 = str(timedelta(days=1)).replace('day,', '')
    SHORT = SHORT_1.replace('  ',' ')
    MEDIUM_1 = str(timedelta(days=3)).replace('days,', '')
    MEDIUM = MEDIUM_1.replace('  ',' ')
    LONG_1 = str(timedelta(days=-5)).replace('days,', '')
    LONG = LONG_1.replace('  ',' ')

    DURATION_CHOICES = ((SHORT, '1 day'),(MEDIUM, '3 days'), (LONG, '5 days'),)
    duration = forms.ChoiceField(widget = forms.Select(), 
                     choices = (DURATION_CHOICES), initial='MEDIUM', required = True,)

    class Meta:
        model = Test
        fields = ['duration']

I don't know it's not recommended or not... I'm too searching why django defaultly not working ...

Zakaria answered 8/4, 2015 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.