Terence Chow
Terence Chow

Reputation: 11173

django formset within a form / dynamic arrays within forms

I have a platform with completely different json configs. Any user can create a new json config that is completely different. Hence I cannot model this staticly.

I am trying to make a dynamic form for these json configs.

Some of the json configs have array of values or objects. Visually, I will need these to be a list of inputs with an 'add more' button.

I see that formsets are the generally accepted way to add arrays of values in django forms. (I'm already using mysql so I can't switch to postgres ArrayField). However from django examples, multiple formsets are initialized separately and passed into the view.

Since my configs are dynamic, I don't know what formsets to initialize and further, the order of fields intermingle between formsets and the form.

For example I am following this link and creating my form classes dynamically like so:

class FieldHandler():
    def __init__(self, fields):
        self.formfields = {}
        for field in fields:
            options = self.get_options(field)
            f = getattr(self, "create_field_for_" +
                        field['type'])(field, options)
            self.formfields[field['name']] = f

    def get_options(self, field):
        options = {}
        options['label'] = field['name']
        options['help_text'] = field.get("help_text", None)
        options['required'] = bool(field.get("required", 0))
        return options

    def create_field_for_text(self, field, options):
        options['max_length'] = int(field.get("max_length", "20"))
        return django.forms.CharField(**options)

    def create_field_for_number(self, field, options):
        options['max_value'] = int(field.get("max_value", "999999999"))
        options['min_value'] = int(field.get("min_value", "-999999999"))
        return django.forms.IntegerField(**options)

    # This does not work, formsets are not shown in the template
    def create_field_for_array(self, field, options):
        fh = FieldHandler(field['elements'])
        form_class = type('DynamicForm',  (django.forms.Form,), fh.formfields)
        DynamicFormSet = formset_factory(form_class)
        return DynamicFormSet(prefix=field['name'])

and in the view:

# arg to FieldHandler is an example config, it will be retrieved from db 
fh = FieldHandler([
    {'type': 'text', 'label': 'position'}, 
    {'type': 'array', 'label': 'calendar', 'elements': [
        {'type': 'text', 'label': 'country'},    
        {'type': 'text', 'label': 'url'},
    ]},
    {'type': 'number', 'label': 'maxSize'}
])
form_class = type('DynamicForm', (django.forms.Form,), fh.formfields)
form = form_class(request.POST or None)

as you can see, the dynamic form is initialized with a config of position, an array of calendar elements and a maxSize field.

My problem is that my dynamic formset is not being shown in the view at all. If I were to separate the formsets, I would somehow have to insert it somewhere inside the original form, so I can't just iterate over the fields, and dynamically create the form first, then dynamically create formsets from the array fields.

Hence I am hoping for a way to dynamically create this type of form, where some fields in the form are formsets.

How do I do this? Or how should I approach this?

Upvotes: 7

Views: 787

Answers (1)

2Pacho
2Pacho

Reputation: 134

If you want to save arrays in MySQL you have to do it as JSON, it's the best way to achieve it. Since you want to save a form that the client generates, and probably recover it afterwards, I would do somenthing like:

import json
from django.db import models

class Forms(models.Model):
      _forms = models.TextField(null=True)

    def get_forms(self):
        if self._forms:
            return json.loads(self._forms)
        return []

    def set_forms(self, v):
        if not v:
            v = []
        self._forms = json.dumps(v)

This way when you call it outside it comes as json already and when u want to save it you pass a json to it.

Upvotes: 1

Related Questions