Django 多项选择字段/复选框选择多个

Posted

技术标签:

【中文标题】Django 多项选择字段/复选框选择多个【英文标题】:Django Multiple Choice Field / Checkbox Select Multiple 【发布时间】:2011-02-13 03:57:15 【问题描述】:

我有一个 Django 应用程序,想在用户的个人资料中显示多项选择复选框。然后他们将能够选择多个项目。

这是我的 models.py 的简化版本:

from profiles.choices import SAMPLE_CHOICES

class Profile(models.Model):
    user = models.ForeignKey(User, unique=True, verbose_name_('user'))
    choice_field = models.CharField(_('Some choices...'), choices=SAMPLE_CHOICES, max_length=50)

还有我的表单类:

class ProfileForm(forms.ModelForm):
    choice_field = forms.MultipleChoiceField(choices=SAMPLE_CHOICES, widget=forms.CheckboxSelectMultiple)

    class Meta:
        model = Profile

还有我的views.py:

if request.method == "POST":
    profile_form = form_class(request.POST, instance=profile)
    if profile_form.is_valid():
        ...
        profile.save()
return render_to_response(template_name, "profile_form": profile_form,, context_instance=RequestContext(request))

我可以看到 POST 只发送一个值:

choice_field u'choice_three' 

并且本地 vars 参数正在发送一个列表:

[u'choice_one', u'choice_two', u'choice_three']

所有表单字段显示正确,但是当我提交 POST 时,我收到错误

错误绑定参数 7 - 可能是不受支持的类型。

我是否需要在视图中进一步处理多项选择字段?模型字段类型是否正确?任何帮助或参考将不胜感激。

【问题讨论】:

您能否针对 POST 时遇到的错误发布完整的堆栈跟踪信息? Django Model MultipleChoice的可能重复 【参考方案1】:

配置文件选项需要设置为 ManyToManyField 才能正常工作。

所以...你的模型应该是这样的:

class Choices(models.Model):
  description = models.CharField(max_length=300)

class Profile(models.Model):
  user = models.ForeignKey(User, blank=True, unique=True, verbose_name='user')
  choices = models.ManyToManyField(Choices)

然后,同步数据库并使用您想要的各种选项加载 Choices。

现在,ModelForm 将自行构建...

class ProfileForm(forms.ModelForm):
  Meta:
    model = Profile
    exclude = ['user']

最后是视图:

if request.method=='POST':
  form = ProfileForm(request.POST)
  if form.is_valid():
    profile = form.save(commit=False)
    profile.user = request.user
    profile.save()
else:
  form = ProfileForm()

return render_to_response(template_name, "profile_form": form, context_instance=RequestContext(request))

应该提到的是,您可以通过几种不同的方式设置配置文件,包括继承。也就是说,这也应该对你有用。

祝你好运。

【讨论】:

谢谢布兰特。您指出我的模型类型需要使用 ManyToManyField 关系是正确的。 请注意,使用 save(commit=false) 时需要在表单实例上调用 save_m2m(),否则对关系的更改将不会被保存。见docs.djangoproject.com/en/dev/topics/forms/modelforms/… 我正在尝试实现这一点,但我收到了NameError: name 'TheChoice' is not defined 该解决方案可以作为元组而不是原始问题中的 Choice 模型适用于 Choice 吗? @brant 也许值得添加一个建议,postgresql 用户可以使用 ArrayFields 来实现相同的功能(甚至更好的性能)【参考方案2】:

Brant 的解决方案绝对正确,但我需要对其进行修改以使其与多个选择复选框和commit=false 一起使用。这是我的解决方案:

models.py

class Choices(models.Model):
    description = models.CharField(max_length=300)

class Profile(models.Model):
   user = models.ForeignKey(User, blank=True, unique=True, verbose_name_('user'))
   the_choices = models.ManyToManyField(Choices)

forms.py

class ProfileForm(forms.ModelForm):
    the_choices = forms.ModelMultipleChoiceField(queryset=Choices.objects.all(), required=False, widget=forms.CheckboxSelectMultiple)

    class Meta:
        model = Profile
        exclude = ['user']

views.py

if request.method=='POST':
    form = ProfileForm(request.POST)
    if form.is_valid():
        profile = form.save(commit=False)
        profile.user = request.user
        profile.save()
        form.save_m2m() # needed since using commit=False
    else:
        form = ProfileForm()

return render_to_response(template_name, "profile_form": form, context_instance=RequestContext(request))

【讨论】:

关于如何在 django 模板中呈现多选字段的任何建议? 确保在 Choices 模型中定义 __str__() 函数,以便描述文本在模板中呈现为复选框选项。【参考方案3】:

models.CharField 是选项之一的 CharField 表示。你想要的是一组选择。这似乎还没有在 django 中实现。

可以为此使用多对多字段,但这样做的缺点是必须将选择放入数据库中。如果您想使用硬编码选项,这可能不是您想要的。

http://djangosnippets.org/snippets/1200/ 有一个 django sn-p,确实似乎通过实现 ModelField MultipleChoiceField 解决了您的问题。

【讨论】:

【参考方案4】:

ManyToManyField 不是一个好的选择。您可以使用一些 sn-ps 来实现 MultipleChoiceField。您可以受到MultiSelectField with comma separated values (Field + FormField) 的启发 但是它有一些bug。你可以安装django-multiselectfield。这个更完美。

【讨论】:

【参考方案5】:

我找到的最简单的方法(只是我使用 eval() 将从输入获取的字符串转换为元组以再次读取表单实例或其他地方)

这个技巧效果很好

#model.py
class ClassName(models.Model):
    field_name = models.CharField(max_length=100)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.field_name:
            self.field_name= eval(self.field_name)



#form.py
CHOICES = [('pi', 'PI'), ('ci', 'CI')]

class ClassNameForm(forms.ModelForm):
    field_name = forms.MultipleChoiceField(choices=CHOICES)

    class Meta:
        model = ClassName
        fields = ['field_name',]

【讨论】:

【参考方案6】:

您可以使用ArrayField 轻松实现此目的:

# in my models...
tags = ArrayField(models.CharField(null=True, blank=True, max_length=100, choices=SECTORS_TAGS_CHOICES), blank=True, default=list)

# in my forms...
class MyForm(forms.ModelForm):

    class Meta:
        model = ModelClass
        fields = [..., 'tags', ...]

我使用tagsinput JS 库来呈现我的标签,但你可以使用任何你喜欢的东西: 这是我的这个小部件的模板:

% if not hidelabel and field.label %<label for=" field.id_for_label "> field.label </label>% endif %
<input id=" field.id_for_label " type="text" name=" field.name " data-provide="tagsinput"% if field.value % value=" field.value "% endif %% if field.field.disabled % disabled% endif %>
% if field.help_text %<small id=" field.name -help-text" class="form-text text-muted"> field.help_text | safe </small>% endif %

【讨论】:

以上是关于Django 多项选择字段/复选框选择多个的主要内容,如果未能解决你的问题,请参考以下文章

渲染 django 表单多项选择

具有未知数量的复选框字段和多个操作的 Django 表单

如何从模型中呈现多项选择字段

在 Streamlit 的复选框中选择多个选项

HTML 多项选择:复选框

使用复选框从 MySQL 中的多个表中选择数据,并根据复选框显示表字段