【问题标题】:Django Forms choice-like field, without choice limitationDjango Forms 类选择字段,没有选择限制
【发布时间】: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 stackoverflow question 的操作,但我宁愿让表单字段本身处理所有保存逻辑。

【问题讨论】:

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


    【解决方案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,可以复制表单集中的字段,并将名称加一。

        【讨论】:

          猜你喜欢
          • 2015-11-08
          • 2018-07-16
          • 1970-01-01
          • 2022-11-10
          • 2016-08-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-22
          • 2014-08-15
          相关资源
          最近更新 更多