Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModelForm + Crispy Forms + Additional Field: clean_FIELD does not receive expeceted data #267

Open
siyb opened this issue Mar 5, 2024 · 0 comments

Comments

@siyb
Copy link

siyb commented Mar 5, 2024

I am using code resembling the following (I omitted a lot of additional fields, labels, help texts, etc to make the example more concise and in turn hopefully more readable). The field emailis defined in the PersonModel whilst email_repeated is a "virtual" field declared and processed within the Person form.

Another Note: I am in the progress of porting our forms to the form wizard, thus, some code might not be necessary any more, i.e. the back button handling inside __init__.

class Person(ModelForm):
    email_repeated = EmailField(
        required=False,
        widget=EmailInput(),
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.field_template = "bootstrap5/field.html"
        self.helper.help_text_inline = False
        self.helper.form_method = "post"
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Fieldset(
                Div("email"),
                Div("email_repeated"),
            ),
            Div(
                Submit("prev", _("⎗ Previous")),
                Submit("next", _("⎘ Next")),
            ),
        )

        # pretend that fields are required for back button management
        self.fields["email"].required = False
        self.fields["email"].show_required = True

        self.fields["email_repeated"].required = False
        self.fields["email_repeated"].show_required = True

    def clean_email_repeated(self):
        print(
            f"clean_email_repeated {self.data.get('email')} -> {self.data.get('email_repeated')} ({self.data})"
        )
        return self.data.get("email_repeated")

    def clean_email(self):
        email = self.get("email")
        email_repeated = self.data.get("email_repeated")
        print(f"clean_email {email} -> {email_repeated} ({self.data})")
        if email != email_repeated:
            self.add_error("email", "E-Mail Error")

        return email

    def clean(self):
        clean_data = super().clean()
        print(f"clean {self.data}")
        return clean_data

    class Meta:
        model = PersonModel
        fields = [
            "email",
            "email_repeated",
        ]
        exclude = ["email_verified"]
        widgets = {
            "email_repeated": EmailInput(),
        }

When using the form in its own view clean_email_repeated prints email as well as email_repeated, when using SessionWizardView only email is printed, email_repeated is None.

Likewise, clean_email_repeated prints both fields correctly when the form in its own view, but prints None when printing email and clean_email_repeated .

clean on the other hand prints both fields correctly. Any hints to what exactly is going on here? Am I to blame? Is this a bug and more importantly, is there a fix that allows me to keep the logic of cleaning the fields separate, without having to couple the forms to SessionWizardView?

// edit: using cleaned_data to access the data also returns None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant