My Django form is not being valid

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.