If you do not call clean
when there is at least one error, you may miss some errors in the final result. For example, if the errors (from clean_fields
) are only for the name
field (e.g. too long value), then by not calling clean
, your model won’t return a potential error for ends_at
being before starts_at
.
However, your use case is legitimate and it may appear cumbersome to check for each field implied in a cross-checking whether the check can be achieved or not (in your case, that would mean verify if starts_at
and ends_at
are instances of datetime.date
before doing comparison).
Another approach if you want to do cross-check only if implied fields are not in errors could be to override clean_fields
like this
def clean_fields(self, excluded=None):
errors = {}
try:
super().clean_fields(excluded=excluded)
except ValidationError as e:
e.update_error_dict(errors)
try:
if "starts_at" not in errors and "ends_at" not in errors and self.starts_at > self.ends_at:
raise ValidationError({"ends_at": _("...")})
except ValidationError as e:
e.update_error_dict(errors)
if errors:
raise ValidationError(errors)
But checking whether fields to check are in errors like in the above is not really simpler than writing:
def clean(self):
if isinstance(self.starts_at, datetime.date) and isinstance(self.ends_at, datetime.date) and self.starts_at > self.ends_at:
raise ValidationError({"ends_at": _("...")})
Moreover, the latter has the advantage of checking consistency between starts_at
and ends_at
if both are dates but one is invalid (for example because it is beyond a max value you may have define on those fields with a validator). To achieve the same with the clean_fields
override example above, you not only have to check whether fields are in errors
or not, but you also need to check what kind of error happened.
Hope that makes sense.