Django形成类似选择的字段,没有选择限制

时间:2013-12-10 02:16:26

标签: django django-forms many-to-many

我正在编写一个基本上只是表单的webapp,并且它有一个复制字段的按钮,以便可以输入多个项目。我不能使用SelectMultiple字段或其任何变体,因为没有一定数量的选择可供选择。用户应该能够在字段中输入他们想要的内容,并且必须将它们保存在模型中并通过manytomany字段链接到记录。这是一个用于演示的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的save方法还能正确保存多个字段?我知道我可以覆盖表单的save方法并执行类似this stackoverflow question的操作,但我宁愿让表单字段本身处理所有保存逻辑。

3 个答案:

答案 0 :(得分:0)

基于您的示例jsfiddle,看起来您并不真正需要&#34;选择字段&#34;,您正在寻找的是Formsets

从本质上讲,你将在页面上有两个表单,一个是普通表单,它将处理字段1,另一个表示处理字段2的所有多对多关系的Formset < / p>

Field2FormSet = formset_factory(Field2ToForm)

确保输出management_form,您可以使用&#34添加&#34;添加&#34;按钮。

答案 1 :(得分:0)

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

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

对于懒惰人来说,这是一个快速的例子,可以帮助你完成大部分工作。此应用程序将允许您连续将父模型对象添加到数据库,以及填写的任何子项。

应用程序/ 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)])

应用程序/ 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因为我认为我不能更好地解释它。

答案 2 :(得分:0)

感谢@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,并将名称增加一个。