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)
Change History (6)
comment:1 by , 3 weeks ago
comment:2 by , 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.
by , 3 weeks ago
| Attachment: | Screenshot 2026-08-04 at 4.32.52 PM.png added |
|---|
by , 3 weeks ago
| Attachment: | Screenshot 2026-08-04 at 4.33.02 PM.png added |
|---|
comment:4 by , 3 weeks ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
If this gets triaged and accepted can I work on it if possible?
Thanks