使用Django Formset从多个模型生成动态表单

时间:2013-05-06 11:52:27

标签: django django-forms django-templates

所以这就是我的问题:我正在尝试生成一个具有许多特征的大型形式,如果它们是布尔值,其中一些将被取消,一些用文本填充,一些用于增加或者用JS微调器递减。但是,这里是捕获,所有这些都是用户生成的。我做的是这样的:在某些时候,我邀请用户通过向模型选项添加对象来创建它的特征,在其中他告诉我特征的名称和它的类型(布尔,文本或数字)。

这是我的简化模型:

  class Characteristic(models.Model):
      option = models.ForeignKey('Option')

  class Option(models.Model):
      option_name = models.CharField(max_length=100)
      OPTION_TYPE_CHOICES = (
          ('B',u'Bolean'),
          ('T',u'Textual'),
          ('N',u'Numerical'),
      )   
      option_type = models.CharField(max_length=1,choices=OPTION_TYPE_CHOICES)

  class Boolean(Characteristic):
      value = models.BooleanField()

  class Textual(Characteristic):
      value = models.CharField(max_length=100)

  class Numerical(Characteristic):
      value = models.PositiveIntegerField()

到目前为止,这么好。

生成具有所有可能特征的表单时,问题就开始了。我做的是这个:

以下是我的观点:

 @login_required
 def AddCharacteristic(request):

     numerical_options = Option.objects.filter(option_type='N')
     textual_options = Option.objects.filter(option_type='T')
     boolean_options = Option.objects.filter(option_type='B')

     NumericalFormset = modelformset_factory(Numerical,extra=len(numerical_options))
     TextualFormset = modelformset_factory(Textual,extra=len(textual_options))
     BooleanFormset = modelformset_factory(Boolean,extra=len(boolean_options),form=forms.BooleanForm)

     if request.method == 'POST':
         numerical_formset = NumericalFormset(request.POST,prefix='numerical')·
         textual_formset = TextualFormset(request.POST,prefix='textual')
         boolean_formset = BooleanFormset(request.POST,prefix='boolean')
         if numerical_formset.is_valid() and textual_formset.is_valid() and boolean_formset.is_valid():
             numerical_formset.save()
             textual_formset.save()
             boolean_formset.save()
         if 'next' in request.GET:
             return HttpResponseRedirect(request.GET['next'])
         else:
             return HttpResponseRedirect('../../list/')
     else:
         boolean_formset = BooleanFormset(queryset=Boolean.objects.none(),prefix='boolean',initial=[{'option':n.id} for n in boolean_options])
         textual_formset = TextualFormset(queryset=Textual.objects.none(),prefix='textual',initial=[{'option':n.id} for n in textual_options])
         numerical_formset = NumericalFormset(queryset=Numerical.objects.none(),prefix='numerical',initial=[{'option':n.id} for n in numerical_options])
     return render_to_response('characteristics/add.html', {
       'numerical_formset': numerical_formset,
       'textual_formset': textual_formset,
       'boolean_formset': boolean_formset,
     },context_instance=RequestContext(request))

在我看来:

 <form  method="post">

     {% csrf_token %}

     {{ numerical_formset.management_form }}
     {% for numerical_form in numerical_formset %}
     {{ numerical_form }}<br>
     {% endfor %}

     {{ boolean_formset.management_form }}
     {% for boolean_form in boolean_formset %}
     {{ boolean_form }}<br>
     {% endfor %}

     {{ textual_formset.management_form }}
     {% for textual_form in textual_formset %}
     {{ textual_form }}<br>
     {% endfor %}
     <button type="submit">Save</button>
     <button type="reset">Cancel</button>
 </form> 

所以,它有点工作,但到目前为止的主要问题是:

  • 我无法重写窗口小部件,将特征选项字段从下拉列表转换为标签。
  • 验证工作。如果用户将文本放在数字字段中或相反,Django就不会抱怨。
  • 一般的感觉,我把它搞砸了,并且过度设计了一些东西。 :)

1 个答案:

答案 0 :(得分:1)

看起来您正在尝试创建自己的EAV实现。您可能希望改为使用django-eav

如果您仍想按自己的方式行事,可以使用EAV dynamic form作为参考。

此外,有一篇名为“Dynamic form generation”的好文章由Django的主要开发人员之一Jacob Kaplan-Moss撰写(该文章于2010年发布,但截至今日,所描述的解决方案是还在工作)。

还有其他存储和检索动态数据的方法(如django-hstoredjango-mutant)。我已经written了解了他们before