Skip to content

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

Open
@siyb

Description

@siyb

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions