Django:如何使表单有条件?

Posted

技术标签:

【中文标题】Django:如何使表单有条件?【英文标题】:Django: How to make form conditional? 【发布时间】:2022-01-19 08:52:19 【问题描述】:

我有一个包含两个字段的表单。应该要求用户只选择两者之一。不是两者都不是。 我尝试通过覆盖 Django Doc 中描述的 clean 方法来解决这个问题:

forms.py

class ConfigureWorkout(forms.Form):
    first_exercise = forms.ModelChoiceField(empty_label="select", label="Choose First Exercise", queryset=FirstExercise.objects.all(), required=False) 
    sec_exercise = forms.ModelChoiceField(empty_label="select", label="Choose Sec Exercise", queryset=SecExercise.objects.all(), required=False)

    def clean(self):
        first_exercise = self.cleaned_data.get("first_exercise")
        sec_exercise = self.cleaned_data.get("sec_exercise")

        if first_exercise and sec_exercise:
            raise forms.ValidationError("Enter either a First Exercise or a Secondary Exercise.")
        else:
            return self.cleaned_data

views.py

def configure(request):
    configure_workout = ConfigureWorkout()

    if request.method == "GET":
        return render(request, "userprofile/some.html", configure_workout)

    else:
        return render(request, "app/other.html")

模板

<form action="% url 'configure' %" method="POST">
    % csrf_token %
     configure_workout 
    <input type="submit" name="configuration_completed">
</form>

但是,如果我通过选择表单中的两个字段来对此进行测试,则不会显示/引发错误。我成功通过了表单并被发送到“other.html”。

我错过了什么? 非常感谢您的帮助:)

【问题讨论】:

您能否检查一下 clean 函数中返回的已清理数据(first_exercisesec_exercise)是否具有您对查询的期望值?以及这个数据有什么数据类型 此外,当覆盖Form.clean() 方法时,文档(Cleaning and validating fields that depend on each other) 声明应该调用super().clean() 我尝试完全按照文档描述的方式进行操作(或者我希望如此),即使使用 'super().clean() 方法也是如此。行为不变。不太确定如何检查返回的清理数据?在 views.py 中调用 print(configure_workout.clean()) 会引发 AttributeError: 'ConfigureWorkout' object has no attribute 'cleaned_data' 【参考方案1】:

您似乎没有将实际数据传递给您的表单。也许这会有所帮助:

def configure(request):
    configure_workout = ConfigureWorkout()

    if request.method == "GET":
        return render(request, "userprofile/some.html", configure_workout)

    else:
        configure_workout = ConfigureWorkout(request.POST)
        configure_workout.is_valid()
        configure_workout.clean()
        return render(request, "app/other.html")

【讨论】:

这会产生一个 AttributeError: "'ConfigureWorkout' object has no attribute 'cleaned_data'",这可以追溯到 first_exercise = self.cleaned_data.get("first_exercise") @Alex clean_data 在你调用is_valid() 方法后设置 太棒了,非常感谢。在clean.() 之前添加is_valid() 解决了这个问题。我现在能够捕获 ValidationError 并在模板中显示错误消息。【参考方案2】:

基于@trafalinos 的回答并查看Forms 上的文档,我建议执行以下操作:

forms.py:

class ConfigureWorkout(forms.Form):
    first_exercise = forms.ModelChoiceField(empty_label="select", label="Choose First Exercise", queryset=FirstExercise.objects.all(), required=False) 
    sec_exercise = forms.ModelChoiceField(empty_label="select", label="Choose Sec Exercise", queryset=SecExercise.objects.all(), required=False)

    def clean(self):
        cleaned_data = super().clean()  # compare documentation
        first_exercise = cleaned_data.get("first_exercise")
        sec_exercise = cleaned_data.get("sec_exercise")

        if first_exercise and sec_exercise:
            raise forms.ValidationError("Enter either a First Exercise or a Secondary Exercise.")
        else:
            return self.cleaned_data

并且(感谢 trafalino)views.py

def configure(request):
    if request.method == "GET":
        configure_workout = ConfigureWorkout()
        return render(request, "userprofile/some.html", configure_workout)

    else:
        configure_workout = ConfigureWorkout(request.POST)
        configure_workout.clean()
        return render(request, "app/other.html")

【讨论】:

以上是关于Django:如何使表单有条件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使登录表单在 Django 的整个站点上可用

Django - 如何使日期选择器在更改时提交表单

Django 向导表单中的条件表单字段

如何使不同的 Django 表单输入按钮显示在同一行

如何使子类化的自定义 Django 表单字段不再是必需的?

提交后如何使表单域为空