I have an app where I am putting an inline_formset
inside of the template of an object form. The formset is for all the related objects. The 'main object' is a GrowthPlan
, the related objects are Response
objects.
When I click 'save', all of the formset objects (Response
) save appropriately without issue.
However, the main object (GrowthPlan
) doesn't save.
The form_invalid(self):
method is getting called (I see my print message in terminal) but there are no errors tied to the form.
The form_valid(self):
method is never getting called.
If I save the form in the post
method, it saves as expected. But since this is an UpdateView
I'd like to handle that with the form_valid
and form_invalid
; or is that the wrong way to go about this?
The UpdateView
looks like this:
class GrowthPlanUpdateView(
LoginRequiredMixin, UserIsObserverOrObserveeMixin, UpdateView
):
model = GrowthPlan
template_name = "commonground/growthplan_form.html"
form_class = GrowthPlanForm
pk_url_kwarg = "growthplan_id"
def get_object(self):
return get_object_or_404(GrowthPlan, pk=self.kwargs["growthplan_id"])
def post(self, request, *args, **kwargs):
self.object = get_object_or_404(
GrowthPlan, pk=self.kwargs["growthplan_id"]
)
regular_form = GrowthPlanForm(
request.POST or None, instance=self.object
)
# IF I UNCOMMENT THIS - THE FORM SAVES APPROPRIATELY
#if regular_form.is_valid():
# # The form saves if I put this line here
# # But the form does not not get to form_valid
# regular_form.save()
# Handle saving the related formsets here
response_formset = ResponseInlineFormset(
request.POST or None,
prefix="response_formset",
instance=self.object,
)
if response_formset.is_valid():
for formset_form in response_formset:
if formset_form.is_valid():
# This works
formset_form.save()
return super().post(request, *args, **kwargs)
def get_form(self):
return self.form_class(instance=self.object)
def form_invalid(self, form):
print("form is invalid")
# THERE ARE NO ERRORS SHOWING HERE - BUT I SEE MY PRINT STATEMENT
print(form.errors)
return super().form_invalid(form)
def form_valid(self, form):
# THIS IS NEVER CALLED
print("getting to form_valid")
form.save()
messages.add_message(
self.request, messages.SUCCESS, "Changes were saved!"
)
return super().form_valid(form)
def get_success_url(self):
user_id = self.kwargs["user_id"]
summative_id = self.kwargs["summative_id"]
evaluation = Evaluation.objects.get(id=self.kwargs["evaluation_id"])
growthplan = evaluation.growthplan
return reverse(
"commonground:growthplan_detail",
kwargs={
"user_id": user_id,
"summative_id": summative_id,
"evaluation_id": evaluation.id,
"growthplan_id": growthplan.id,
},
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
summative = get_object_or_404(
Summative, pk=self.kwargs["summative_id"]
)
if self.request.user != summative.employee:
context["user_is_observer"] = True
if self.request.user == summative.employee:
context["user_is_observer"] = False
context["employee"] = summative.employee
try:
context[
"employee_information"
] = EmployeePayInformation.objects.get(user=summative.employee)
except EmployeePayInformation.DoesNotExist:
context["employee_information"] = None
except EmployeePayInformation.MultipleObjectsReturned:
# TODO: Log error
context["employee_information"] = None
context["response_formset"] = ResponseInlineFormset(
prefix="response_formset", instance=self.object
)
context["growthplan_is_complete"] = False
if self.object.is_complete:
context["growthplan_is_complete"] = True
return context
My form HTML looks like this:
<form method="post" novalidate>
{% csrf_token %}
{{ form.as_p }}
{{ response_formset.management_form }}
{{ formset.management_form }}
{% for formset_form in response_formset.forms %}
{{ formset_form.as_p }}
{% endfor %}
<button type="submit" class="btn btn-primary"><i class="fal fa-clipboard-check"></i> Update Growth Plan</button>
</form>
The forms.py forms look like this:
class GrowthPlanForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(GrowthPlanForm, self).__init__(*args, **kwargs)
class Meta:
model = GrowthPlan
fields = "__all__"
widgets = {
"updated_by": forms.HiddenInput(),
"evaluation": forms.HiddenInput(),
"created_by": forms.HiddenInput(),
"initial_date": DatePickerInput(attrs={"class": "form-control"}),
"mid_date": DatePickerInput(attrs={"class": "form-control"}),
"final_date": DatePickerInput(attrs={"class": "form-control"}),
"expected_growth": forms.TextInput(
attrs={"class": "form-control"}
),
"actual_growth": forms.TextInput(attrs={"class": "form-control"}),
}
class ResponseForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ResponseForm, self).__init__(*args, **kwargs)
self.fields["question"].queryset = Question.objects.filter(
id=self.instance.question.id
)
self.fields["question"].empty_label = None
class Meta:
model = Response
fields = "__all__"
widgets = {
"user": forms.HiddenInput(),
"growthplan": forms.HiddenInput(),
"question": forms.HiddenInput(),
"comment": forms.Textarea(attrs={"class": "form-control"}),
}
def clean(self):
cleaned_data = super().clean()
comment = cleaned_data.get("comment")
growthplan = cleaned_data.get("growthplan")
if growthplan.is_complete and not comment:
# The comment is required because it should be complete
self.add_error(
"comment",
"""
You must enter a comment before the Growth Plan can be
completed.
""",
)
ResponseInlineFormset = inlineformset_factory(
GrowthPlan,
Response,
fields=("question", "comment", "user", "growthplan"),
can_delete=False,
extra=0,
form=ResponseForm,
)
Should I be handling this in the post
method and ignoring the form_valid
and form_invalid
methods?
Should I be using a different CBV?
Why would it be coming up form_invalid()
when the form data is actually valid?
I'm scratching my head - any insight is appreciated!
For what it's worth - if I removed the get_form
override from the GrowthPlanUpdateView
it worked as intended and the form is marked as valid/invalid as expected now.