自定义小部件中的键/值对

时间:2012-12-09 07:27:20

标签: python django

所以我想说我有jQuery从这个

转换一个简单的文本输入
<input class='employee_list' name='requestor' type='text' />

到这个

<div name='requestor' class='ajax_picker'>
    <input class='search_box' class='employee_list' name='requestor_text' type='text'/>
    <input class='id' name='requestor' type='hidden' value='' /> 
    <div class='results'></div>
</div>

使用AJAX + JSON加载隐藏的输入值。问题是,if form.is_valid() is not True,那我怎样才能创建一个可以呈现两个值的自定义Widget?我想我可以做两个不同的领域,但这看起来很难看。我也可以自定义表单渲染,但更糟糕的是。也许表单可以将所有POST数据传递给窗口小部件,但似乎无法弄清楚如何使其工作。

必须有一种优雅的方式来实现这一目标!

from django import forms

class AjaxPickerWidget(forms.TextInput):
    def render(self, name, value, attrs=None):
        # ... now what?
        return super(AjaxPickerWidget, self).render(name, value, attrs=attrs)

我的解决方案

感谢您的帮助,我采取了变体方法。由于我严格地将它用于模型(因此需要一个键/值对),我使得小部件直接与模型交互并创建一个data-id属性,jQuery将捕获该属性以移动到隐藏字段。

from django import forms

class AjaxPickerModelWidget(forms.TextInput):
    def __init__(self, css_class, queryset, id_name, value_name, attrs={}):
        attrs['class'] = css_class
        self.queryset = queryset
        self.id_name = id_name
        self.value_name = value_name
        super(AjaxPickerModelWidget, self).__init__(attrs=attrs)

    def render(self, name, value, attrs={}):
        try:
            instance = self.queryset.get(**{self.id_name: value})
            attrs['data-id'] = value
            value = getattr(instance, self.value_name)
        except:
            value = ''
        return super(AjaxPickerModelWidget, self).render(name, value,
                                                         attrs=attrs)

2 个答案:

答案 0 :(得分:1)

嗯,我不知道这是否符合优雅,但是这样的事情怎么样。在这里,我将搜索字符串存储为窗口小部件上的属性,并在窗体构造函数中设置POST数据的属性。我还使用name =“foo_picker_text”来指示名为“foo”的相应输入的搜索输入。

class AjaxPickerWidget(HiddenInput):
  search_text_suffix = "_picker_text"

  def __init__(self, **kwargs):
    super(AjaxPickerWidget, self).__init__(**kwargs)
    self.search_text = None

  def render(self, name, value, attrs=None):
    start = '<div name="%s" class="ajax_picker">' % (name) + \
             '<input class="search_box" name="%s%s" ' % (name, self.search_text_suffix)+ \
             'type="text" value="%s" />' % (self.search_text or "")
    end = super(AjaxPickerWidget, self).render(name, value, attrs=attrs) + '</div>'
    return '%s%s' % (start, end)

class MyForm(Form):
  requestor = CharField(widget = AjaxPickerWidget())

  def __init__(self, *args, **kwargs):
    super(MyForm, self).__init__(*args, **kwargs)
    # find picker text in POST data and tell the corresponding widget about it
    for name in self.data:
      if name.endswith(AjaxPickerWidget.search_text_suffix):
        field_name = name.split(AjaxPickerWidget.search_text_suffix)[0]
        self.fields[field_name].widget.search_text = self.data[name]

您可能需要调整一下才能让属性显示在您想要的位置等等,但希望这基本上可以满足您的需求吗?

答案 1 :(得分:0)

您可以代替在表单上调用is_valid()来创建自定义函数

from django import forms

class AjaxPickerWidget(forms.TextInput):
    def render(self, name, value, attrs=None):
        return super(AjaxPickerWidget, self).render(name, value, attrs=attrs)
    def validate(self):
        values = self.cleaned_data.items():
        # Modify the values to fit the original form (delete, change etc)
        return whateversleft.is_valid() # Perhaps with some additional checks