How to create an ical duration field in Django?

烈酒焚心 提交于 2019-12-12 01:16:05

问题


I use a Django model that I register with the admin site. One of the fields of my model represents a duration. I would like to use the DateTimeField, but instead of saving the value to a datetime in the database, I would like to save it as varchar, formatted according to RFC5545 (ical) (e.g., a duration of 1 day 1 hour 1 min 1 sec would be stored as "P1DT1H1M1S"). How would I do this? Should I overwrite the DateTimeField?


回答1:


You could create a custom Django field for it instead of overwriting DateTimeField.

https://docs.djangoproject.com/en/dev/howto/custom-model-fields/




回答2:


Yep, just subclass model.Field. And define two methods:

Field.to_python(self, value) - will convert db value to python object.

Field.get_prep_value(self, value) - this is opposite to to_python converts object to db value.




回答3:


Thanks bakkal and Pol. Below is what I came up with.

from django.db import models
from icalendar.prop import vDuration
from django.forms.widgets import MultiWidget
from django.forms import TextInput, IntegerField
from django.forms.util import flatatt
from django.forms.fields import MultiValueField
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _
from django.core import validators
from datetime import timedelta

def is_int(s):
    try: 
        int(s)
        return True
    except ValueError:
        return False

class Widget_LabelInputField(TextInput):
    """
    Input widget with label
    """
    input_type="numbers"
    def __init__(self, labelCaption, attrs=None):
        self.labelCaption = labelCaption
    super(Widget_LabelInputField, self).__init__(attrs)

    def _format_value(self, value):
        if is_int(value):
            return value
        return '0'

    def render(self, name, value, attrs=None):
        if value is None:
            value = '0'
        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
        if value != '':
            # Only add the 'value' attribute if a value is non-empty.
            final_attrs['value'] = force_unicode(self._format_value(value))
        if (self.labelCaption):
        typeString = self.labelCaption + ': '
        else:
            typeString = ''           
        return mark_safe(u'' + typeString + '<input%s style=\'width: 30px; margin-right: 20px\'/>' % flatatt(final_attrs))



class Widget_DurationField(MultiWidget):
    """
    A Widget that splits duration input into two <input type="text"> boxes.
    """

    def __init__(self, attrs=None):
        widgets = (Widget_LabelInputField(labelCaption='days', attrs=attrs),
                   Widget_LabelInputField(labelCaption='hours', attrs=attrs),
                   Widget_LabelInputField(labelCaption='minutes', attrs=attrs),
                   Widget_LabelInputField(labelCaption='seconds', attrs=attrs)
                   )
        super(Widget_DurationField, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            duration = vDuration.from_ical(value)
            return [str(duration.days), str(duration.seconds // 3600), str(duration.seconds % 3600 // 60), str(duration.seconds % 60)]
        return [None, None, None, None]



class Forms_DurationField(MultiValueField):
    widget = Widget_DurationField
    default_error_messages = {
        'invalid_day': _(u'Enter a valid day.'),
        'invalid_hour': _(u'Enter a valid hour.'),
        'invalid_minute': _(u'Enter a valid minute.'),
        'invalid_second': _(u'Enter a valid second.')
    }

    def __init__(self, *args, **kwargs):
        errors = self.default_error_messages.copy()
        if 'error_messages' in kwargs:
            errors.update(kwargs['error_messages'])
        fields = (
            IntegerField(min_value=-9999, max_value=9999,
                      error_messages={'invalid': errors['invalid_day']},),
            IntegerField(min_value=-9999, max_value=9999,
                      error_messages={'invalid': errors['invalid_hour']},),
            IntegerField(min_value=-9999, max_value=9999,
                      error_messages={'invalid': errors['invalid_minute']},),
            IntegerField(min_value=-9999, max_value=9999,
                      error_messages={'invalid': errors['invalid_second']},),
        )
        super(Forms_DurationField, self).__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if data_list:
            if data_list[0] in validators.EMPTY_VALUES:
                raise ValidationError(self.error_messages['invalid_day'])
            if data_list[1] in validators.EMPTY_VALUES:
                raise ValidationError(self.error_messages['invalid_hour'])
            if data_list[2] in validators.EMPTY_VALUES:
                raise ValidationError(self.error_messages['invalid_minute'])
            if data_list[3] in validators.EMPTY_VALUES:
                raise ValidationError(self.error_messages['invalid_second'])

            return vDuration(timedelta(days=data_list[0],hours=data_list[1],minutes=data_list[2],seconds=data_list[3]))
        return None




class Model_DurationField(models.Field):
    description = "Duration"

    def __init__(self, *args, **kwargs):
        super(Model_DurationField, self).__init__(*args, **kwargs)

    def db_type(self, connection):
        return 'varchar(255)'

    def get_internal_type(self):
        return "Model_DurationField"

    def to_python(self, value):
        if isinstance(value, vDuration) or value is None:
            return value

        return vDuration.from_ical(value) 

    def get_prep_value(self, value):
        return value.to_ical() 

    def formfield(self, **kwargs):
        defaults = {
            'form_class': Forms_DurationField,
            'required': not self.blank,
            'label': capfirst(self.verbose_name),
            'help_text': self.help_text}
        defaults.update(kwargs)
        return super(Model_DurationField, self).formfield(**defaults)


来源:https://stackoverflow.com/questions/13597063/how-to-create-an-ical-duration-field-in-django

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!