多小部件是否可以使用两个不同的模型字段

时间:2012-12-27 20:21:42

标签: django model field django-multiwidget

我一直在拉我的头发来完成这件事。我的一个模型中有2个整数字段,如下所示:

#models.py

class TestModel(models.Model):
    """
    This is a test model.
    """

    max = models.IntegerField()
    min = models.IntegerField()

由于以上两个值是相互关联的,我希望他们使用自定义小部件。现在重点是,我希望这些字段使用单个自定义窗口小部件(MultiWidget),但我也希望将值保存在数据库的不同列中[不要真正想解析内容,而且搜索功能将使用以上字段分开,所以只想单独存储]。

自定义窗口小部件只接受来自单个模型字段的值,因此我决定创建一个自定义窗体域,以便在ModelForm中用于上述模型。

现在这就是我的表格。

class MinMaxField(forms.MultiValueField):

    def __init__(self, *args, **kwargs):

        all_fields = ( 
            forms.CharField(),
            forms.CharField(),
            )   

        super(MinMaxField, self).__init__(all_fields, *args, **kwargs)

    def compress(self, values):
        if values:
            return '|'.join(values)
        return ''

class TestModelForm(forms.ModelForm):

    minmax = MinMaxField(widget=widgets.MinMaxWidget(),
             required=False)

    class Meta:
        model = models.TestModel
        fields = ('min', 'max', 'minmax')
        widgets = {
            'min' : forms.HiddenInput(),
            'max' : forms.HiddenInput(),
        }

    def full_clean(self, *args, **kwargs):
        if 'minmax_0' in self.data:
            newdata = self.data.copy()
            newdata['min'] = self.data['minmax_0']
            newdata['max'] = self.data['minmax_1']
            self.data = newdata
        super(TestModelForm, self).full_clean(*args, **kwargs)

上面的表单基本上隐藏了模型字段,并且只显示了使用自定义窗口小部件的minmax字段,如下所示:

class MinMaxWidget(widgets.MultiWidget):

    def __init__(self, attrs=None):
        mwidgets = (widgets.Select(attrs=attrs, choices=MIN),
                   widgets.Select(attrs=attrs, choices=MAX))
        super(MinMaxWidget, self).__init__(mwidgets, attrs)

    def value_from_datadict(self, data, files, name):
        try:
            values = [widget.value_from_datadict(data, files, name + '_%s' % i)\ 
                      for i, widget in enumerate(self.widgets)]

            value = [int(values[0]), int(values[1])]
        except ValueError:
            raise ValueError('Value %s for %s did not validate. \
                              a list or a tuple expected' % (value, self.widget))
        return value

    def decompress(self, value):
        if value:
            if isinstance(value, list):
                return value
            return [value[0], value[1]]
        return [None, None]

    def format_output(self, rendered_widgets):
        rendered_widgets.insert(0, '<span class="multi_selects">')
        rendered_widgets.insert(-1, '<label id="min">Min</label>')
        rendered_widgets.append('<label id="max">Max</label></span>')
        return u''.join(rendered_widgets)

现在,此时一切正常,值会分别保存到各自的字段中。但是,当我尝试使用TestModel对象的实例编辑表单时,问题就出现了。在编辑的情况下,在呈现表单时,实际值存储在隐藏的minmax输入字段中,而minmax字段没有值。 [当然,因为它是一个表格领域。]

我有什么选择

  1. 预先填充minmaxmin字段中max表单字段的值。 [注意:不使用Javascript或jQuery]

  2. 或者创建一个使用两个不同模型字段中的值的MultiWidget。

  3. 任何帮助将不胜感激。感谢。

1 个答案:

答案 0 :(得分:0)

我目前遇到类似的问题,并且有一种方法可以将窗口小部件用于两个字段。 我使用的是1.4版,并没有尝试过任何其他版本。

在您看来,您可能会遇到以下情况:

form = TestModelForm(instance=instance)

其中instance是您要编辑的数据库中的对象。 让我们看看Django在创建表单时正在做什么

# django/forms/models.py

class BaseModelForm(BaseForm):
     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                  initial=None, error_class=ErrorList, label_suffix=':',
                  empty_permitted=False, instance=None):
          ...
          if instance is None:
              ...
          else:
              self.instance = instance
              object_data = model_to_dict(instance, opts.fields, opts.exclude)
          # if initial was provided, it should override the values from instance
          if initial is not None:
              object_data.update(initial)
          ...
          super(BaseModelForm, self).__init__(data, files, auto_id, prefix, 
                                              object_data, error_class, 
                                              label_suffix, empty_permitted)

然后,在呈现表单时,BoundField类使用此object_data为窗口小部件分配初始值。

因此,您需要做的就是在表单构造函数中提供initial

class TestModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        if 'instance' in kwargs:
            initial = dictionary with initial values
            initial.update(kwargs['initial'] or {})
        ...
        super(TestModelForm, self).__init__(initial=initial, *args, **kwargs)

另请记住将您的字段添加到表单fields列表中。

这样你就没有两个未使用的隐藏小部件,不需要使用javascript和imo,代码更具说明性。