自定义 Django 模型字段中的“对象没有属性”
Posted
技术标签:
【中文标题】自定义 Django 模型字段中的“对象没有属性”【英文标题】:"object has no attribute" in custom Django model field 【发布时间】:2012-11-18 02:09:00 【问题描述】:我正在尝试创建一个 Django 模型字段,该字段表示 html 中包含天、小时、分钟和秒文本输入字段的持续时间,并使用 ical 格式 (RFC5545) 将持续时间存储在数据库中。
(这与我在How to create an ical duration field in Django? 上的问题有关)
这是我的方法:
感谢 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。有什么想法吗?
【问题讨论】:
【参考方案1】:我是愚蠢的。问题是我将定义模型的文件命名为duration.py,因此与“duration”字段存在命名冲突。我重命名了文件,它工作了。
【讨论】:
我遇到了类似的问题。我将视图命名为与模型相同的名称。【参考方案2】:您更改模型后是否运行./manage syncdb
?
如果您决定更改模型,它不会更新当前数据库。 唯一的方法是重置它,然后运行syncdb。
如果您使用的是 SQLite - 只需删除 db 文件,运行 syncdb,它将使用您更新的模型生成一个数据库。请注意,它将删除所有现有记录!
【讨论】:
以上是关于自定义 Django 模型字段中的“对象没有属性”的主要内容,如果未能解决你的问题,请参考以下文章
自定义 Django 小部件/字段:对象没有属性“attrs”
Django allauth 自定义登录表单未呈现自定义用户模型中的所有字段