I have a form for a Challenge model + inline formsets for each hint. Something like this:
# forms.py
class CreateHintInlineFormSet(BaseInlineFormSet):
def clean(self, *args, **kwargs):
super(CreateHintInlineFormSet, self).clean(*args, **kwargs)
total_penalization = 0
for form in self.forms:
if form.cleaned_data.get("DELETE", False):
continue
if (
form.cleaned_data.get("text", None) is None
or form.cleaned_data.get("penalization", None) is None
):
raise ValidationError(
_("You must specify a text and a penalization for each hint")
)
else:
total_penalization += int(form.cleaned_data["penalization"])
if total_penalization > self.instance.points:
raise ValidationError(
_(
"The total penalization of the hints cannot be greater than the challenge points"
)
)
class CreateHintForm(ModelForm):
class Meta:
model = Hint
fields = ("text", "penalization")
widgets = {
"text": TextInput(attrs={"rows": 1}),
}
HintFormSet = inlineformset_factory(
model=Hint,
parent_model=Challenge,
formset=CreateHintInlineFormSet,
form=CreateHintForm,
extra=0,
can_delete=True,
)
Then my view is as follows:
class CreateEditChallengeView(
LoginRequiredMixin,
SingleObjectTemplateResponseMixin,
ModelFormMixin,
ProcessFormView,
AdminInstructorRoleRequired,
):
model = Challenge
template_name = "challenges/create-edit-challenge.html"
form_class = CreateChallengeForm
success_url = reverse_lazy("challenges-list")
def get_context_data(self, **kwargs):
context = super(CreateEditChallengeView, self).get_context_data(**kwargs)
context["title"] = (
_("New challenge") if self.object is None else _("Edit challenge")
)
context["scenarios_table"] = ScenarioInfoTable(self.get_available_scenarios())
context["mode"] = "edit" if self.object else "create"
context["formset"] = HintFormSet(
self.request.POST or None, instance=self.object or None
)
return context
def get_available_scenarios(self):
return Scenario.objects.for_organization(self.request.user)
def get_object(self, queryset=None):
try:
if "challenge_id" in self.kwargs:
pk = object_pretty_id_to_db_identifier(self.kwargs.get("challenge_id"))
return Challenge.objects.get(pk=pk)
return super(CreateEditChallengeView, self).get_object(queryset)
except AttributeError:
return None
def get(self, request, *args, **kwargs):
self.object = self.get_object() # noqa
return super(CreateEditChallengeView, self).get(request, *args, **kwargs)
def form_invalid(self, form, formset=None):
context = self.get_context_data()
context["form"] = form
if formset is not None:
context["formset"] = formset
return self.render_to_response(context)
def form_valid(self, form):
context = self.get_context_data()
formset = context["formset"]
formset.instance = form.save(commit=False)
if not formset.is_valid():
return self.form_invalid(form, formset=formset)
self.object = form.save() # noqa
formset.save()
return HttpResponseRedirect(self.get_success_url())
def post(self, request, *args, **kwargs):
self.object = self.get_object() # noqa
return super(CreateEditChallengeView, self).post(request, *args, **kwargs)
Challenge model and its corresponding Hints are created just fine, but when trying to update any of the Hint objects, it creates a new Challenge object with no Hints associated.
I tried to print if the inline formset is recieving the correct values and indeed it is, but it does not update the Hint model.
{'text': 'Hi', 'penalization': 7, 'challenge': <Challenge: First challenge>, 'id': <Hint: Hi>, 'DELETE': False}
{'text': 'Bye', 'penalization': 7, 'challenge': <Challenge: Second challenge>, 'id': <Hint: Bye>, 'DELETE': False}
Here I’m trying to update each Hint penalization from 1 (value I set when I created them) to 7. Formset gets the values, but won’t update the objects.
What could be the problem?