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