Dynamically defining Form's at runtime from a schema

I’m developing a website for use in education and it requires tracking a lot of different kinds of evaluation forms. It doesn’t make sense to define a Model/Form per evaluation type.

My plan is to create a subclass of BaseForm that, in addition to its parameters, takes a schema which defines the form’s fields and their attributes. It would otherwise behave just like a regular Form class. I would then save the contents of cleaned_data in a JSON field.

This would be roughly what I’m planning to do:

from django import forms
class DynamicForm(forms.BaseForm):
  def __init__(self, *args, **kwargs):
    schema = kwargs.pop('schema')

    # define `dynamic_fields` based on this schema
    # ...

    self.base_fields = dynamic_fields
    super(DynamicForm, self).__init__(*args, **kwargs)

According to Django’s documentation, we should only use fields to customize the fields of a form, not base_fields. But from reading the source code, I believe that modifying base_fields is the only way to go for my design. The caution against changing base_fields seems to only apply when the form subclass is derived through a metaclass, which I’m planning to use in my design.

Am I on the right track or should I be reconsidering my design?

Any suggestions would be appreciated. I looked at a few existing options for “dynamic forms” but none of them seemed to match my requirements.

The caution against changing base_fields applies anywhere you’re creating a future instance of the form. The attribute base_fields is stored as an attribute of the class and not the instance that you are working with. So if you create a new field, every instance of that form you create in the same process after this will have that new field.

Copied from the source code:

#The base_fields class attribute is the class-wide definition of
# fields. Because a particular instance of the class might want to
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.

So if you want to add fields on a per-instance basis, you can add the fields to the fields object in the __init__ method of your form - or you can add them in the view after the form has been created.

Thanks Ken!

The reason I asked is because base_fields is only set as a class attribute in DeclarativeFieldsMetaclass, which I don’t plan to use in my DynamicForm. DynamicForm is meant to be instantiated directly with the schema passed to it as an argument as opposed to subclassed with the field defined as attributes on the subclass.

The reason I wanted to use base_fields instead of fields is that fields gets overwritten with base_fields in BaseForm.__init__.

I could call super first and then overwrite fields. However, this misses some components of the initialization, specifically the call to order_fields which may modify fields. I guess I could just re-run that order_fields call and I’ll need to get the field_order argument passed into __init__ in the kwargs. Of course this could break if BaseForm.__init__ changes in the future.

I was trying to think of the “cleanest” and most generic to implement the class so it could then be published as a small reusable package, but I’ll just go with a pragmatic solution for now.

As I think about this some more, I’m wondering what there is to be gained by even trying to do this.

It sounds to me like each of these forms are already defined - I’m not seeing where there’s any advantage to not just defining these as forms.

Yes, I understand you may not want individual models - that’s ok. But a form doesn’t need to be tied to a model. You can create these forms as forms, and store the responses as a JSON structure within a model.