我正在尝试创建一个 Django 模型字段,该字段表示 HTML 中包含天、小时、分钟和秒文本输入字段的持续时间,并使用 ical 格式(RFC5545)将持续时间存储在数据库中。
(这与我关于如何在 Django 中创建 ical 持续时间字段的问题有关?)
这是我的方法:
感谢 bakkal 和 Pol。下面是我想出的。
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)
它适用于以下模型:
class TestModel(models.Model):
ID = models.CharField(max_length=255)
start = models.DateTimeField(null=True)
#duration = models.CharField(max_length=255,null=True) commented out
otherDuration = duration.Model_DurationField(null=True)
但不是在这个:
class TestModel(models.Model):
ID = models.CharField(max_length=255)
start = models.DateTimeField(null=True)
duration = models.CharField(max_length=255,null=True) # not commented out
otherDuration = duration.Model_DurationField(null=True)
我收到以下错误:
File "/somepath/models.py", line 5, in TestModel
otherDuration = duration.Model_DurationField(null=True)
AttributeError: 'CharField' object has no attribute 'Model_DurationField'
这让我感到困惑......似乎python认为我的字段是前一个字段的属性,但前提是它是一个CharField。有任何想法吗?