Opened 3 weeks ago

Last modified 3 weeks ago

#37245 assigned Bug

ArrayField model validators stop validation at first failing array item

Reported by: Chris Owned by: Vishy
Component: contrib.postgres Version: 5.2
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The validation of the ArrayField model field stops validation of the array at the first element failing validation.

The provided form field classes instead continue validation and collect all errors from all elements.

If only the model validation fails, then only the first failing element is reported. This might happen if a custom base_field is used which does not have a corresponding form field, or does not add its validators to the form field, or just does some custom validation which is for whatever reason not present or possible in the form, etc.

The model validation should produce the same results as the form validation.

The ArrayField methods validate() and run_validators() methods should not immediately re-raise a (prefixed) ValidationError but collect the error into an error_list, continue with the rest of the array elements, and at the end raise one ValidationError with the error_list (if any errors occurred).

Attachments (2)

Screenshot 2026-08-04 at 4.32.52 PM.png (161.6 KB ) - added by Sidarth Nuthi 3 weeks ago.
Screenshot 2026-08-04 at 4.33.02 PM.png (101.6 KB ) - added by Sidarth Nuthi 3 weeks ago.

Download all attachments as: .zip

Change History (6)

comment:1 by Yassin Bahri, 3 weeks ago

If this gets triaged and accepted can I work on it if possible?
Thanks

comment:2 by Sidarth Nuthi, 3 weeks ago

I was able to reproduce this behavior with the following script:

import django
from django.db import models
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError


settings.configure(
    DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
    INSTALLED_APPS=["django.contrib.contenttypes", "django.contrib.postgres"],
)
django.setup()

class MyModel(models.Model):
    numbers = ArrayField(
        models.IntegerField(validators=[MinValueValidator(0)])
    )
    class Meta:
        app_label = "myapp"


obj = MyModel(numbers=[-1, 2, -3, 4, -5])
try:
    obj.full_clean()
except ValidationError as e:
    print(e.message_dict)

This code snippet will print

    {'numbers': ['Item 1 in the array did not validate: Ensure this value is greater than or equal to 0.']}

Even though items 3 and 5 also fail the validator and are not included in the resulting ValidationError. This is consistent with the ticket description: ArrayField.validate() stops at the first failing item instead of collecting validation errors from all items.

Looking at how ValidationErrors are usually handled for lists (example from django/contrib/auth/password_validation.py validate_password function or django/contrib/auth/forms.py validate_passwords function) - when a list is passed in, Django usually returns a list of validation errors.

A similar approach may be appropriate here, collecting all the prefixed errors while iterating through the array, then raise a single ValidationError containing all the collected errors after the validation is done.

Version 0, edited 3 weeks ago by Sidarth Nuthi (next)

by Sidarth Nuthi, 3 weeks ago

by Sidarth Nuthi, 3 weeks ago

comment:3 by Sarah Boyce, 3 weeks ago

Triage Stage: UnreviewedAccepted

Thank you for the report!

comment:4 by Vishy, 3 weeks ago

Owner: set to Vishy
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top