Django Forms 类选择字段,没有选择限制

Posted

技术标签:

【中文标题】Django Forms 类选择字段,没有选择限制【英文标题】:Django Forms choice-like field, without choice limitation 【发布时间】:2013-12-27 09:35:24 【问题描述】:

我正在编写一个基本上只是一个表单的 web 应用程序,它有一个复制字段的按钮,以便可以输入多个项目。我不能使用 SelectMultiple 字段或其任何变体,因为没有固定数量的选项可供选择。用户应该能够在字段中输入他们想要的任何内容,并且必须将它们保存在模型中并通过多对多字段链接到记录。这是用于演示的jsfiddle 链接。

html

<form>
    <label>Field 1
        <textarea rows="3"></textarea>
    </label>
    <label>Multiple Values Possible</label>
    <div>
        <input type="text">
        <select>
            <option value="1">1</option>
            <option value="2">2</option>
        </select>
    </div>
    <button id="add_div">Add</button>
</form>

JS

add_div = document.getElementById("add_div");
add_div.onclick = function () 
    var div = this.previousElementSibling;
    var new_div = div.cloneNode(true);

    this.parentNode.insertBefore(new_div, this);

    return false;
.bind(add_div);

我不知道如何为此创建表单后端。没有任何字段类可以接收可变数量的数据并根据另一个字段验证每个数据。

我试图做的是为文本框/选择下拉对创建一个 MultiWidget/MultiValueField,然后在紧跟 django 的 ModelMultipleChoiceField 的类中子类化我的 MultiValueField。我试图让表单字段与模板一起使用时遇到了困难,允许我在使用特定表单实例进行渲染时将所有字段添加回呈现的页面(例如,当您使用 CheckboxSelectMultiple 小部件时,在表单实例中选中的框渲染检查)

有没有办法做到这一点,并且让 ModelForm 的保存方法也正确保存多对多字段?我知道我可以覆盖表单的保存方法并执行类似this *** question 的操作,但我宁愿让表单字段本身处理所有保存逻辑。

【问题讨论】:

【参考方案1】:

基于查看您的示例 jsfiddle,您似乎并不需要“选择字段”,您正在寻找的是 Formsets。

本质上,页面上有 2 个表单,一个是普通表单,负责处理字段 1,另一个是处理字段 2 的所有多对多关系的表单集

Field2FormSet = formset_factory(Field2ToForm)

确保输出management_form,您可以使用“添加”按钮进行克隆。

【讨论】:

【参考方案2】:

您可能正在寻找的是 inline formset,它只能在您使用 django 模型时使用(您在问题中暗示过)。

查看本指南:http://lab305.com/news/2012/jul/19/django-inline-formset-underscore/。

对于懒惰的人,这里有一个简单的示例,可以帮助您了解大部分情况。此应用程序将允许您将父模型对象以及已填写的任何子模型对象不断添加到数据库中。

app/models.py

from django.db import models


class ParentModel(models.Model):
    parent_field = models.CharField(choices=[(1, 1), (2, 2)])


class ChildModel(models.Model):
    parent = models.ForeignKey(ParentModel)
    child_field = models.IntegerField(choices=[(1, 1), (2, 2)])

app/views.py

from app import models
from django import forms
from django.forms.models import inlineformset_factory
from django.template import RequestContext, Template
from django.http import HttpResponse


class ParentForm(forms.ModelForm):

    class Meta:
        model = models.ParentModel


ChildFormSet = inlineformset_factory(models.ParentModel, models.ChildModel)


def test_view(request):
    if request.method == "POST":
        form = ParentForm(request.POST, request.FILES)
        formset = ChildFormSet(request.POST, request.FILES, form.instance)
        if form.is_valid() and formset.is_valid():
            form.save()
            formset.save()
        else:
            pass # Handle validation error
    template = Template(
        "<form action='<url for view>' method='post'>"
             "% csrf_token %"
             " form.as_p "
             "<p> formset.as_table </p>"
             "<input type='submit' value='Submit'/>"
        "</form>"
    )
    context = RequestContext(request, 
        "form": ParentForm(),
        "formset": ChildFormSet(),
    )
    return HttpResponse(template.render(context))

上面显示的内容只允许您添加最多三个子项(内联表单集生成的额外表单的默认数量)。要动态添加,您将不得不添加一些在客户端设置的表单中创建新表单的 java 脚本。为此,我建议您查看the guide I posted above,因为我认为我无法更好地解释它。

【讨论】:

【参考方案3】:

感谢@Kevin 和@Thomas 将我指向表单集!这是我的做法:

models.py

​​>
from django.db import models

class RelatedField(models.Model):
    field1 = models.CharField(max_length=50)
    field2 = models.IntegerField(choices=[(x, x) for x in xrange(1, 11)])

class Record(models.Model):
    user     = models.ForeignKey(User)
    field    = models.CharField(max_length=20)
    relatedA = models.ManyToManyField(RelatedField, related_name='relatedA')
    relatedB = models.ManyToManyField(RelatedField, related_name='relatedB')

views.py

​​>
def getIndexContext(data):
    if data is None:
        recordForm  = RecordForm()
        relatedFormA = RelatedFormSet(queryset=RelatedField.objects.none(), prefix='related-a')
        relatedFormB  = RelatedFormSet(queryset=RelatedField.objects.none(), prefix='related-b')
    else:
        recordForm  = RecordForm(data)
        relatedFormA = RelatedFormSet(data, prefix='related-a')
        relatedFormB  = RelatedFormSet(data, prefix='related-b')

    return 
        'form': recordForm,
        'relatedA': relatedFormA,
        'relatedB': relatedFormB,
        'title': 'Index',
       


def index(request):
    if request.method == 'GET':
        return render(request, 'record/index.html', getIndexContext(None))
    else:
        context = getIndexContext(request.POST)
        form = context['form']
        relatedA = context['relatedA']
        relatedB = context['relatedB']
        if form.is_valid() and relatedA.is_valid() and relatedB.is_valid():
            obj = form.save(commit=False)
            obj.user_id = request.user 
            obj.save()
            form.save_m2m()

            instances = relatedA.save()
            obj.relatedA.add(*instances)

            instances = relatedB.save()
            obj.relatedB.add(*instances)

            return HttpResponse('success!')
        else:
            return render(request, 'record/index.html', context)

然后是一些 javascript,可以复制表单集中的字段,并将名称加一。

【讨论】:

以上是关于Django Forms 类选择字段,没有选择限制的主要内容,如果未能解决你的问题,请参考以下文章

django-select2:如果没有选择国家,如何禁用城市选择? (django 2.2)

django 基础知识 ~ forms详解

django中的日期时间选择器不使用forms.py

如何使用 Django 的表单框架来选择选项?

如何将 django forms.py 选择项转换为 html 中的列表?

如何根据 django admin 中的另一个选择字段限制选择字段选项