Hi All! I have a variation of a scenario featured in previous posts, but can’t seem to make the final mental jump to my specific scenario.
I have a model Assessment
that has some basic properties as well as a JSON model field definition_json
that stores json data describing the details of the assessment.
For example, if the Assessment model is type MULTIPLE_CHOICE
, the definition_json
might have a dictionary of data with answers, like this:
{
"choices": [
{
"text": "Answer #1. This one is correct.",
"choice_key": "a",
"is_correct": True
},
{
"text": "Answer #2. This one is also correct.",
"choice_key": "b",
"is_correct": True
},
{
"text": "Answer #3. This one is not correct",
"choice_key": "c",
"is_correct": False
}
]
}
So I want to provide an “author” user with a form (not a Django admin form, just part of the regular UI) to configure this kind of Assessment.
The form has to show the regular Assessment fields (like ‘name’) and then a list of each existing answer as an editable line item: text field for choice_key
, text field for answer text
, checkbox for whether is_correct
or not)…as well as a delete button to remove any answer. Finally, a button beneath the answer list to add a new row for each new answer the author wants to add.
All the user’s work should then submit as one POST to be processed by the form.
I’m starting with a ModelForm for the Assessment model, but not sure how to enable editing/adding/deleting answers, all of which would be operating on the json data from the definition_json
JSON field.
I’m also not sure how to read in, parse and store that data once the main form is submitted.
As far as I can tell, I should use a formset factory, but I’m not sure how to integrate it into my form.
( From what I gather inlineformset_factory
is only for parent <> child model relationships and can’t be used in this situation. )
This is my guess so far…
class MultipleChoiceAnswerMultiWidget(forms.widgets.MultiWidget):
widgets = {
'text': forms.TextInput(),
'choice_key': forms.TextInput(),
'correct': forms.CheckboxInput(),
}
def decompress(self, value):
if value:
return [value['text'], value['choice_key'], value['correct']]
else:
return [None, None, None]
class MultipleChoiceAnswerMultiValueField(forms.MultiValueField):
def __init__(self, *args, **kwargs):
fields = (
forms.CharField(),
forms.CharField(),
forms.BooleanField(),
)
super().__init__(fields, *args, **kwargs)
def compress(self, data_list):
return {
'text': data_list[0],
'choice_key': data_list[1],
'correct': data_list[2],
}
class MultipleChoiceAnswerForm(forms.Form):
question_definition = MultipleChoiceAnswerMultiValueField(
required=False,
)
MultipleChoiceAnswerFormSet = formset_factory(MultipleChoiceAnswerForm, extra=0)
class MultipleChoiceAssessmentPanelForm(ModelForm):
""" Form for editing an Assessment of type MULTIPLE_CHOICE """
name = forms.TextField()
choices = MultipleChoiceAnswerFormSet()
class Meta:
model = Assessment
fields = []
....
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
choices.initial = self.instance.definition_json
....
… and the template …
{% load crispy_forms_tags %}
{% csrf_token %}
{% crispy form %}
…but not sure how or where to introduce template code and javascript to handle deleting existing rows or adding a new, empty row?
Or could it be the better approach is just to make a single widget that takes in the complete definition_json and manually creates input fields, delete buttons and add button?
I have a feeling Django already has the machinery for managing a CRUD list like this within one model, but I’m not sure how it relates here (I think it’s meant for parent <> child model relationships).
Sorry this is a bit disjointed. Thanks for any suggestions!