如何根据用户选择的方式不同地渲染django表单?

时间:2014-03-25 12:31:06

标签: django

我有一个模型和这样的表格:

class MyModel(models.Model):
    param = models.CharField()
    param1 = models.CharField()
    param2 = models.CharField()

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ('param', 'param1', 'param2')

然后我有一个具有不同值的下拉菜单,根据选择的值,我隐藏并显示MyForm的字段。现在我必须更进一步,如果用户从下拉列表中选择某个值,则将param2渲染为CheckboxInput小部件,但在其他情况下,它应该是标准文本字段。那我该怎么做呢?

2 个答案:

答案 0 :(得分:3)

我知道这篇文章已经差不多一年了,但我花了好几个小时甚至找到了一个与这个主题相关的帖子(这是我发现的唯一一个,提交我自己的问题时提到的那个),所以我觉得有必要分享我的解决方案。

如果下拉菜单中的选项与另一个模型中存储的值匹配,我想要一个可以显示并需要文本字段的表单。我在两个模型之间有一个foreignKey关系,我将Model1的一个实例传递给Model2的ModelForm。如果为Model2中的变量选择的值与Model1中已设置的变量匹配,我想显示并需要一个文本字段。它基本上是“选择其他,然后输入您自己的描述”方案。

我不希望页面重新加载(我试图在移动和桌面浏览器中使用最少的延迟/重新加载和使用相同的代码两者),所以我无法使用提到的多个表单加载在视图选项中。当我意识到自己已经在思考这个问题时,我开始尝试使用上面建议的AJAX。

答案是在表单中使用JS和clean方法。我在Model2Form中添加了一个不在Model2中的非必需字段(field1)。然后我使用jQuery隐藏它,只显示它(使用jQuery),如果另一个字段(field2)的值与Model1中的变量值匹配。为了做到这一点,我确实决定隐藏< span>在我的模板中使用变量的pk,所以我可以轻松地使用jQuery抓取它。这个jQuery非常适合隐藏和正确显示字段,因此用户可以选择“其他”值,然后决定选择另一个值(并且无休止地来回)。

然后我在我的Model2Form中使用了一个clean1方法,当field2中的值与我的Model1变量匹配时,如果没有输入值,则会引发ValidationError。我在__ init __方法中使用“self.other = Model1.variable”访问该变量,然后在clean_field1方法中引用该变量。

我本来希望能够在不必隐藏和显示JS字段的情况下完成此操作,但我认为使用视图或ajax这样做的唯一解决方案会导致我不想要的延迟/重新加载。此外,我喜欢我使用的方法的一般简单性,而不是必须弄清楚如何通过HTTPRequest来回传递部分表单。

<强>更新
在我的情况下,我正在为丢失和找到的项目创建条目,如果找到项目的位置不是提​​供的选项,那么我想显示一个文本框供用户输入该位置。我创建了一个位置对象,该对象被设置为“其他”位置,然后在该对象被选为“找到”位置时显示文本框。

在forms.py中,我添加了一个额外的CharField并使用一个干净的方法来检查该字段是否是必需的,如果没有填写则抛出ValidationError:

class Model2Form(forms.ModelForm):
    def __init__(self, Model1, *args, **kwargs):
        self.other = Model1.otherLocation
        super(Model2Form, self).__init__(*args, **kwargs)
        ...

    otherLocation = forms.CharField(
        label="Location Description",
        max_length=255,
        required=False
    )

    def clean_otherLocation(self):
        if self.cleaned_data['locationFound'] == self.other and not self.cleaned_data['otherLocation']:
            raise ValidationError("Must describe the location.")
        return self.cleaned_data['otherLocation']

然后在我的JavaScript中,我检查了“找到”位置的值是否是“其他”位置(我在html页面上隐藏的跨度中的值)。然后我根据需要在文本框的父元素上使用.show()和.hide():

$("#id_locationFound").change( function(){
    if ($("#id_locationFound").val() == $("#otherLocation").attr("value")){ //if matches "other" location, display textbox; otherwise, hide textbox
        $("#id_otherLocation").parent().show();
    }else
        $("#id_otherLocation").parent().hide();
});

答案 1 :(得分:0)

你最好的猜测是触发&#34; POST&#34;当您从下拉菜单中选择某些内容时请求。

该值的价值&#34; POST&#34;必须与您用于确定要输出的字段的值相对应。

现在你实际上需要两种形式:

class MyBaseForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ('param', 'param1', 'param2')

class MyDropDownForm(MyBaseForm):
    class Meta:
        widgets = {
            'param2': Select(attrs={...}),
        }

因为您可以看到DropDownForm是从MyBaseForm派生的,以确保它具有所有相同的属性。但是我们修改了其中一个字段的小部件。

现在您可以更新您的视图了。请注意,这是未经测试的Python + Pseudocode

<强> views.py

def myFormView(request):
  if request.method == 'POST': # If the form has been submitted...    
    form = MyBaseForm(request.POST)

    #submit button has not been pressed, so the dropdown has triggered the submission. 
    #Hence we won't safe the form, but reload it            
    if 'my_real_submitbotton' not in form.data:     

      if 'param1' == "Dropdown":
         form = MyDropDownForm(request.POST)

    else:
      #do your normal form saving procedure
  else:
    form = ContactForm() # An unbound form

  return render(request, 'yourTemplate.html', {
    'form': form,
  })

此机制执行以下操作: 提交表单时,它会检查您是否按下了&#34;提交&#34;按钮或使用下拉按钮更改以触发提交。我的解决方案不包含使用onChange触发提交所需的javascript代码。我只是想提供一种解决方法。 要使用&#39; my_real_submit按钮&#39;在form.data构造中,您将需要命名您的提交按钮:

<input type="submit" name="my_real_submitbutton" value="Submit" />

当然,您可以选择任何字符串作为名称。 :-)

如果您的下拉字段提交,您必须检查在此下拉菜单中选择了哪个值。如果此值满足您想要返回下拉菜单的条件,则创建一个DropDownForm实例(request.POST),否则您可以保留所有内容并重新呈现模板。

在缺点方面,这将刷新您的页面。 在好的方面,它将保留所有已输入的字段值。所以这里没有伤害。

如果您想避免页面刷新,您可以保留我提出的想法,但您需要通过AJAX呈现新表单。