1

So here is my problem: I'm trying to generate a big form of with a bunch of characteristics, some of them to be ticket it off if they are boolean, some to be filled with text and some to be incremented or decremented with a JS spinner. But, and here is the catch, all of them are user generated. What I did was this: In some point I invite the user to create it's characteristics by adding objects to the model Option, in which he tells me the name of the characteristics and it's type (boolean, textual or numerical).

Here is my simplified model:

  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()

So far, so good.

The problem starts when generating that form with all the possible characteristics. What I did was this:

Here is my view:

 @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))

And in my view:

 <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> 

So, it's kind of working, but the main issue so far is:

  • I'm not being able to rewrite the widget, transforming the characteristic option field from a dropdown to a label.
  • Validation to work. If the user put text in the numerical field or the other way around, Django it's not complaining.
  • The general feeling that I mess it up completely and over engineered something. :)
rrb_bbr
  • 2,966
  • 4
  • 24
  • 26

1 Answers1

1

It really looks like you are trying to create your own implementation of EAV. You might want to go with django-eav instead.

If you still want to go your own way, you can use EAV dynamic form as a reference.

Also, there is a nice article called "Dynamic form generation" written by Jacob Kaplan-Moss, one of the lead developers of Django (the article was published in 2010, but, as of today, the described solution is still working).

There are also other approaches for storing and retrieving dynamic data (Like django-hstore and django-mutant). I have already written about them before.

Community
  • 1
  • 1
Ivan Kharlamov
  • 1,889
  • 2
  • 24
  • 33
  • As a sidenote: there is also an app called `django-hstore-flattenfields` which uses a [hack](https://github.com/multmeio/django-hstore-flattenfields/blob/master/hstore_flattenfields/models.py) to make Django [generate](https://github.com/multmeio/django-hstore-flattenfields/blob/master/hstore_flattenfields/forms.py) dynamic forms as if they were `ModelForm`s. – Ivan Kharlamov May 06 '13 at 15:34