I have a form and a dependent formset. On the browser, the required fields on the form provide an instant visual feedback when the user omits the value. I don’t get the same behaviour from the fields in the formset.
The only way the user knows that the formset failed is that the view renders again with the rejected values. There is no hint about the failing field. Whereas, there are visual feedbacks for the form on the same page.
How do I get the fields in the formset to behave like those in form?
I am not sure you understood the challenge. The three forms with darker gray backgrounds are not extras. Only the last one in that set is an extra. The first two are not. They are part of a specified minimum.
The form with fairer gray background is for the Parent. The ParentForm is showing feedback (“Please fill in this field”). Now, when I satisfy the ParentForm without providing values to any of the SiblingForms (the ?-marks or formset), and hit the Add button, the submission fails (as espected) and the page reloads with the previous values on the ParentForm
Now the Challenge is:
How do I get the SiblingForm (at least the two that are required, not the extra) to show the same feedback as ParentForm? Must I rely on formset.errors? I thought members of a formset provide instant field validation.
I think what is happening here is that the form is not submitting. The browser-level validation is actually happening, but I do not see a nice balloon (feedback) on any formset field. I populated the ParentForm with required values already. So, I’m guessing Django is deciding to stay on the page without highighting omitted fields (since they’re many).
Once I provide values to the formset fields, the formset validation (clean()) kicks in. It seems I was expecting too much.
Which implies that the form is being submitted but is failing the server-side validation, with:
Check your server log - is there a POST request or not? (The page isn’t reloading if it’s not being submitted and validated on the server with the response containing the error messages.)
If not - if the form is not submitting, then this has something to do with how the form is being rendered within the formset.
In either case, it would be interesting to see the rendered html to compare the differences between the parent form and the formset.
Sorry for the confusion. I can confirm that the form is not submitting. There is no POST request output on the terminal. Submission happens after I provide values to members of the formset. I was wishing that there is a sort of notification (feedback) at the point of refusal – just like with the Parent form.
The real project sample is heavy. I will try and replicate this at a smaller scale. Stay tuned
No worries - I’m only trying to understand the situation to (hopefully) give good advice.
I don’t think you need to go that far yet.
If you just copy the html of the parent form and the html of the first form in the formset from your browser’s developer tools, that would be very interesting to see.
The key point here is that unless there’s a visible difference in the fields being rendered, this is a browser issue and not a Django issue. Django itself has nothing to do with browser-side validation, other than rendering (or failing to render) the appropriate field attributes to make it possible.
So I recreated the scenario at small scale and got the shock of my life. The form was submitting! I went back to the problematic instance and payed closer attention. It was also submitting. @KenWhitesell please forgive my confusion.
POST/GET messages are arriving. The thing is actually submitting and getting rendered again with the values. I am using a FormView Class.
from django.views.generic import FormView
from django.urls import reverse
from .models import Parent, Child
from .forms import (
ParentForm,
ChildForm,
ParentChildInlineFormset,
)
# Create your views here.
class CaseView(FormView):
form_class = ParentForm
template_name = "sample/case.html"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if "formset" not in ctx:
ctx.update(
{
"formset": ParentChildInlineFormset(
instance=ctx["form"].instance
),
}
)
return ctx
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
formset = ParentChildInlineFormset(
self.request.POST,
self.request.FILES,
)
if not formset.is_valid():
print(formset.errors)
return self.render_to_response(self.get_context_data(form=form, formset=formset))
self.success_url = reverse("root:welcome")
return super().form_valid(form)
But I found basis for the confusion. The formset failed because the two minimum (required) ChildForms were empty – no ChildForm had values. Django registers this omission as an error in formset.errors for each missig field and the View reroutes to render again. This happens silently.
Why was I confused?
The clean() method of the formset does not execute. Therefore the formset hasn’t performed cleaning. How then is it setting formset.errors? What happens when I run formset.is_valid()? This was the source of confusion.
How is it that the formset detects field errors but does not notify the user before submission (like with the Parent form)? It allows a submission before the form gets routed back for re-render (all with no hint that a required field was empty). I thought formset.is_valid() triggers cleaning.
Summary:
field validation on formet is happening – but field errors are silently stored in formset.errors formset.is_valid() does not trigger clean() on the formset – which is why I thought submission did not occur.
I am marking this as solved as per the highlighted post. As it turned out, the formset was submitting and error messages were sending the view back for another render. I realize it is my responsibility to use create feedback with those error messages.