Django 根据 ModelForm 中的 ChoiceField 选择填充多对多

Posted

技术标签:

【中文标题】Django 根据 ModelForm 中的 ChoiceField 选择填充多对多【英文标题】:Django Populate ManyToMany according to a ChoiceField selection within a ModelForm 【发布时间】:2014-05-27 03:03:50 【问题描述】:

我有一个具有 ManyToMany 关系的类,我想用它来为应用创建控制面板:

class ControlPanel(models.Model):
    viewtype = models.CharField("view type", max_length=32, unique=True)    
    choice1 = models.ManyToManyField(Choice1, null=True)
    choice2 = models.ManyToManyField(Choice2, null=True)
    choice3 = models.ManyToManyField(Choice3, null=True)

其中Choice1Choice2Choice3 是我想呈现为复选框的选项。 controlpanel_choice1controlpanel_choice2controlpanel_choice3 是带有 Choice1Choice2Choice3 选项选项的 M2M 关系表。因此,根据视图类型 ID,对于每个 Choice* 集,选择集可能不同(甚至不存在)。

我从这个 ControlPanel 类创建了一个表单来生成我的控制面板:

class ControlPanelForm(ModelForm):
    viewtype = forms.ChoiceField(choices = ControlPanel.objects.values_list('id','view'))
    choice1 = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Choice1.objects.all())
    choice2 = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Choice2.objects.all())
    choice3 = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Choice3.objects.all())

    class Meta:
        model = ControlPanel

这会很好地显示 3 选择* 字段。但是,它并不限制对每个视图类型选项的选择,因为我的查询集为每个 choice* 字段显示 .objects.all()。此外,这会显示对象地址,而不是每个 Choice* 的名称字段。 我怎样才能做到这一点?我需要为绑定和未绑定表单显示 Choice*。我是否需要在构造函数中构建它们,检查我的表单是否有效并获取当前选择的视图类型来构建我的查询集?我可以在我的ControlPanel 类中有这个逻辑,并避免在我的视图函数中编程吗?我会很感激一些例子来做到这一点。

提前致谢。

【问题讨论】:

【参考方案1】:

首先你需要创建一个接受参数的表单

class ControlPanelForm(ModelForm):
    class Meta:
        model = ControlPanel
        widgets = 
            'choice1': forms.CheckboxSelectMultiple(),
            'choice2': forms.CheckboxSelectMultiple(),
            'choice3': forms.CheckboxSelectMultiple(),
        

    def __init__(self, *args, **kwargs):
        viewtype = kwargs.pop('viewtype')
        super(ControlPanelForm, self).__init__(*args, **kwargs)
        self.fields['choice1'].queryset = Choice1.objects.filter( ... )
        self.fields['choice2'].queryset = Choice2.objects.filter( ... )
        self.fields['choice3'].queryset = Choice3.objects.filter( ... )

如您所见,我已将 viewtype 作为位置参数传递给表单的 init,现在您可以使用它来创建带有 filter() 的查询集,而不是使用 all()

此外,当使用ModelForm 时,无需再次声明表单字段。如果您需要更改任何字段的小部件,使用Meta 类的widgets 选项似乎更合适。

现在,要在您的视图中使用此表单,只需将 viewtype 作为位置参数传入即可。

viewtype = request.POST['viewtype']
form = ControlPanelForm(viewtype=viewtype)

你现在要做的就是把它放在一个视图中并调用它,每次传入一个不同的viewtype

【讨论】:

嗨,非常感谢这些指针,非常有用。如果我想为 viewtype 设置一个默认值(当有一个未绑定的表单时),我应该在视图中传递这个值,还是可以在 ControlPanelForm 类中声明它? 我已经尝试了你的建议,开始看到光明!如果我理解得很好,则无法访问 ModelForm 实例(?)中小部件的选定值,因此我必须将其作为位置参数传递。我也明白 viewtype=kwargs.pop('viewtype') 只是从 ModelForm 的 init 中删除了一个无效参数。此外,我可以使用另一个名称(例如 viewnb)来弹出参数并将此变量用于我的过滤器。保持所有相同的名称很方便。这是正确的吗? 因此,对于默认视图值,我刚刚测试了 request.POST.has_key('viewtype')。如果不是,则使用默认值实例化表单,我在构造函数中传递 viewtype='viewtype':'1' ,'1' 是创建表单时的默认值(我可以使用 request.method= ='POST' 测试是否在没有 POST 的情况下访问带有表单的页面)。 当您说您已经测试了请求以查看它是否包含viewtype 这是否意味着每次用户选择视图类型时整个页面都会刷新? 不,如果这是您的意思,我还没有尝试过 AJAX 部分。用户更改视图类型后需要提交新表单。这个我稍后会实现。【参考方案2】:

只是对这个问题的跟进:我意识到使用 javascript/jQuery 在客户端实现这种功能更合乎逻辑。事实上,由于 html 的非持久性方面,在服务器端有这样的逻辑是非常尴尬和容易出错的。我在学习基本 jQuery 时遇到了很多麻烦,并且有一种在浏览器端隐藏/显示选项和结果的好方法。

【讨论】:

以上是关于Django 根据 ModelForm 中的 ChoiceField 选择填充多对多的主要内容,如果未能解决你的问题,请参考以下文章

基于下拉值选择显示Django ModelForm中的链接

Django之ModelForm详解

如何根据在模型级别定义的枚举字段在 Django Modelform 中呈现单选按钮选择?

Django中的ModelForm无法接收数据

如何过滤 Django ModelForm 中的 ManyToManyField 选项?

Django-Form组件-forms.ModelForm