Cool! that’ll work great.
The simplest data structure that can work here is a dict.
Example:
camera_fields = {
'Camera model': {
'type': forms.CharField,
'params': {
'max_length': 40,
'help_text': 'Full camera model name'
}
},
'date': {
'type': forms.DateTimeField,
'params': {
'widget': forms.SplitDateTimeWidget
}
},
'location': {
'type': forms.CharField,
'params': {
'label': 'Picture location',
'max_length': 50,
'help_text': 'Location where the picture was taken'
}
}
}
You can then define a form as:
class CameraForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for k,v in camera_fields.items():
self.fields[k] = v['type'](**v['params'])
However, in your case, let’s assume you only want a subset of fields, where the list of field names is built in the view. You can pass that list of names as a parameter when constructing the form.
Your form may then end up looking like this:
class CameraForm(forms.Form):
def __init__(self, field_list, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name in field_list:
self.fields[field_name] = camera_fields['field_name']['type'](**camera_fields['field_name']['params'])
(Yes, that’s the complete form definition. All fields are being dynamically created.)
If you run this in the shell, then printing the generated form is going to produce something like this:
<p>
<label for="id_Camera model">Camera model:</label>
<input type="text" name="Camera model" maxlength="40" required aria-describedby="id_Camera model_helptext" id="id_Camera model">
<span class="helptext" id="id_Camera model_helptext">Full camera model name</span>
</p>
<p>
<label>Date:</label>
<input type="text" name="date_0" required id="id_date_0">
<input type="text" name="date_1" required id="id_date_1">
</p>
<p>
<label for="id_location">Picture location:</label>
<input type="text" name="location" maxlength="50" required aria-describedby="id_location_helptext" id="id_location">
<span class="helptext" id="id_location_helptext">Location where the picture was taken</span>
</p>
This handles the simple case. If you need to construct your fields in a way that this simple formatting does not allow (usually when you need to provide more complex widget definitions), then you can turn this camera_fields
dict into a set of classes.
There are also a number of different ways where you can provide “overrides” for this on a per-instance basis, usually by passing those overrides in as additional parameters. This requires some changes to the __init__
method, but still doesn’t end up being too ugly.
(And yes, I’ve got at least four projects I can think of off-hand where I use this technique heavily - especially with Crispy Forms, since I can define the layout requirements for these fields and completely build out the form from a data structure.)