Hi there,
My django form is not being validated, i.e. means I receive False as the result of calling is_valid()
method.
I have tried to replace the form with the one that is working in another view, but that too did not work.
My forms.py is:
class SwitchWorkspaceForm(forms.ModelForm):
workspaces = forms.ChoiceField(
label='Workspaces',
widget=forms.RadioSelect,
choices=workspaces_choices
)
def __init__(self, request, USER_WORKSPACES, *args, **kwargs):
super (SwitchWorkspaceForm, self).__init__(*args, **kwargs)
self.fields['workspaces'] = forms.ChoiceField(
label='Workspaces',
widget=forms.RadioSelect,
choices=USER_WORKSPACES
)
class Meta:
model = Workspace
fields = ("workspaces",)
and my views.py
is:
class SwitchWorkspace(View):
USER_WORKSPACES = []
success_url = "/accounts/home/"
template_name = "accounts/switch-workspace.html"
def get(self, request: HttpRequest) -> HttpResponse:
if request.user.is_superuser:
return redirect(self.success_url)
self.USER_WORKSPACES = request.user.get_workspaces_choices()
switch_workspace_form = SwitchWorkspaceForm(request, self.USER_WORKSPACES)
context = {
"title": "Switch workspace",
"switch_workspace_form": switch_workspace_form
}
return render(request, self.template_name, context)
def post(self, request: HttpRequest) -> HttpResponse:
if request.user.is_superuser:
return redirect(self.success_url)
self.USER_WORKSPACES = request.user.get_workspaces_choices()
switch_workspace_form = SwitchWorkspaceForm(request.POST, self.USER_WORKSPACES)
context = {
"title": "Switch workspace",
"switch_workspace_form": switch_workspace_form
}
if not switch_workspace_form.is_valid():
print(switch_workspace_form.errors)
messages.error(
request,
"ERROR: You did not fill all the \
fields of the form or you did not fill\
the form properly."
)
return render(
request,
self.template_name,
context
)
choice = switch_workspace_form.cleaned_data["workspaces"]
request.user.update_active_workspace(choice)
return redirect(self.success_url)
Can anyone help mw what’s wrong? I can’t see what’s wrong because a similar form is working fine in another view.
If this is the is_valid
test that is failing, what is getting printed in this print statement?
It is printing nothing, and if I change print(switch-workspace-form.errors)
to ``print(switch-workspace-form)`, I see an HTML code of the form printed.
And this get printed on the front-end too at the top of the page
@KenWhitesell
UPDATE:
I converted my form from forms.ModelForm
to forms.Form
, and for testing purpose I am entering the manual data in choices
field to see if everything is working fine. Now my form looks like following:
class SwitchWorkspaceForm(forms.Form):
workspaces = forms.ChoiceField(
widget=forms.RadioSelect,
choices=(("DataMeister", "DataMeister"), ("PricingMeister", "PricingMeister"))
)
def __init__(self, request, *args, **kwargs):
super (SwitchWorkspaceForm, self).__init__(*args, **kwargs)
self.fields['workspaces'] = forms.ChoiceField(
label="DataMeister",
widget=forms.RadioSelect,
choices=(("DataMeister", "DataMeister"), ("PricingMeister", "PricingMeister"))
)
and in my views.py
the form initialization for both get()
and post()
methods looks like this:
class SwitchWorkspace():
def get(self, request: HttpRequest) -> HttpResponse:
.
.
.
switch_workspace_form = SwitchWorkspaceForm(request)
.
.
.
def post(self, request: HttpRequest) -> HttpResponse:
.
.
.
switch_workspace_form = SwitchWorkspaceForm(request.POST)
Now, the interestig thing is that, if I remove the __init__()
method from my form definition, it works absolutely fine, but with __init__()
the testing of is_valid()
method fails. So I guess, something is wrong with __init__()
method. Can you help me now to find what’s wrong in __init__()
method?
Finally after 7.5 hours of debugging, I solved the issue by making the following changes.
Changed the views.py
to:
class SwitchWorkspace(View):
success_url = "/accounts/home/"
template_name = "accounts/switch-workspace.html"
def get(self, request: HttpRequest) -> HttpResponse:
if request.user.is_superuser:
return redirect(self.success_url)
USER_WORKSPACES = request.user.get_workspaces_choices()
request.session["workspaces"] = USER_WORKSPACES
switch_workspace_form = SwitchWorkspaceForm(request)
context = {
"title": "Switch workspace",
"switch_workspace_form": switch_workspace_form
}
return render(request, self.template_name, context)
def post(self, request: HttpRequest):
if request.user.is_superuser:
return redirect(self.success_url)
switch_workspace_form = SwitchWorkspaceForm(
request,
request.POST,
)
context = {
"title": "Invite a user",
"switch_workspace_form": switch_workspace_form,
}
if not switch_workspace_form.is_valid():
messages.error(request, f"ERROR: You did not fill \
the form properly.")
return render(
request,
self.template_name,
context
)
form_data = switch_workspace_form.cleaned_data
choice = form_data.cleaned_data["workspaces"]
request.user.update_active_workspace(choice)
return redirect(self.success_url)
and forms.py
to:
class SwitchWorkspaceForm(forms.Form):
workspaces = forms.ChoiceField(
widget=forms.RadioSelect,
choices=(("DataMeister", "DataMeister"), ("PricingMeister", "PricingMeister"))
)
def __init__(self, request, *args, **kwargs):
super (SwitchWorkspaceForm, self).__init__(*args, **kwargs)
if "workspaces" in args:
self.fields['workspaces'] = forms.ChoiceField(
label="Your Workspaces",
widget=forms.RadioSelect,
choices=request["workspaces"]
)
else:
self.fields['workspaces'] = forms.ChoiceField(
label="Your Workspaces",
widget=forms.RadioSelect,
choices=request.session["workspaces"]
)
@KenWhitesell I want to ask that, is it fine if I have solved the issue this way? Am I following any bad practice here?
That works. I don’t see any problem with it.
What I would have originally pointed out is that given the degree of metaprogramming that occurs within forms, I wouldn’t try to add extra parameters to the form creation as positional parameters. I would add them as new keyword parameters - making sure to remove them from the parameter set before calling super.
eg:
my_form = SomeForm(request.POST, my_parm="Some value")
Then in the form, it’s:
def __init__(self, *args, **kwargs):
my_parm = kwargs.pop('my_parm', None)
super().__init__(*args, **kwargs)
# do something with my_parm
...
This ensures that the parameter doesn’t leak into the actual form construction process. That’s the pattern we use when needing to pass custom information into a form.