自定义 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 自定义登录表单未呈现自定义用户模型中的所有字段

Django自定义模型(model)中的字段标签

从显示自定义字段的 Django 模型呈现可排序表的最佳方法?

如何在 Django 会话模型中设置自定义字段?

Django中的自定义纬度/经度表单字段