ValidationError in clean_<fieldname> does not immediately render the error?

I have a form which I simplified for brevity’s sake. It takes a csvfile and some other fields, that I simplified away.

class ImportForm(Form):
    csvfile = FileField(
        validators=[
            FileMaxSizeValidator(1024 * 1024 * 1024),
            FileExtensionValidator(["csv"]),
        ]
    )

    def clean_csvfile(self):
        ...  # Simplified for brevity

        # Removed the error condition for simplification
        raise ValidationError("CSV field {} missing.".format(rf))

        csvfile = list(csvreader)
        return csvfile

    def clean(self):
        cleaned_data = super(ImportForm, self).clean()
        csvfile = cleaned_data.get("csvfile")
        if(len(csvfile) > 1):
             ...

        ... # Shortened for brevity

I am expecting the form to render immediately after the clean_csvfile() method raises the ValidationError, and show the error message CSV field {} missing. in the form.

What actually happens instead is that the clean() method is called, which throws object of type 'NoneType' has no len() at len(csvfile).

I’m confused because I think this was working fine, but I was surprised with the app throwing. But then again, I did some major refactoring and haven’t touched that particular piece of code for some weeks. So maybe I just never tested that code properly with a faulty csvfile.

Is it correct that ValidationErrors in a clean_() still cause clean() to run afterwards? I’m confused and could need some clarification on what’s happening.

Thanks.

The form validation process is detailed at Form and field validation | Django documentation | Django

Briefly, all validators are run before anything is returned in order to provide as much information to the view as possible. (It would be annoying at best if errors were only revealed one at a time.)

Yeah, this happens.
I had opened a discussion a little time ago here on this forum as well about this behavior.

Understood, thank you.

I tried moving my code entirely into clean() and then remember that I split it in the first place because I like that the error is attributed to the input field, which is rendered in a nice way.

I know that I can use self.add_error("csvfile", "The error message to display at the field") from within clean() to achieve a similar thing. But I like the clear separation. So here is a follow-up question.

What is the canonical way if I want to check if a field had a validation error from within clean()? Is it good to test if self.cleaned_data("csvfile") == None or should I use "csvfile" in self.errors.keys() or are there better ways?

There’s two ways (that i think) to approach this on the clean method.

  1. Check if the field name is on the self.errors. E.g: "field_name" in self.errors
  2. Do your checks considering that the data is not “clean”, so it can be missing, having invalid values.
1 Like