Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 625
Terence Chow Avatar asked Jul 17 '18 05:07

Terence Chow


People also ask

How do I create a dynamic form set in Django?

Django is for server-side code, so formsets are static by default, but we can use JavaScript to add some life to them. Before we start slinging some code, let’s take a moment and map out what we need to do to create a dynamic formset. Create an “Add Another Image” button. Clone and insert an new form. Increment the management form.

What is formset in Django?

A formset is a collection of Django Forms. Each formset has a management form which is used to manage the collection of forms contained in it. Formset stores data like the total number of forms, the initial number of forms and the maximum number of forms in a management form.

How to create multiple instances of a form in Django?

Django formsets are used to handle multiple instances of a form. One can create multiple forms easily using extra attribute of Django Formsets. In geeks/views.py, The keyword argument extra makes multiple copies of same form. If one wants to create 5 forms enter extra = 5 and similarly for others.

How to use modelformset_factory () in Django?

Instead, we use Django's modelformset_factory () which returns a formset class for a given model. modelformset_factory () requires the first argument be the model the formset is for. After specifying the model, different optional arguments can be specified. We'll use two optional arguments - fields and extra .


1 Answers

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.

like image 182
2Pacho Avatar answered Oct 12 '22 22:10

2Pacho