diff --git a/docs/changelog.md b/docs/changelog.md index c37838e..24b8c01 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [UNRELEASED] +### Fixed +- Fix the `attr` of errors raised on the first item (index `0`) of a `many=True` list serializer losing its `0.` + index prefix with DRF 3.18, since `ListSerializer.to_internal_value` now collects child errors in a dict keyed by + the integer index of the item, and integer `0` was being treated as falsy. ## [0.16.0] - 2026-04-29 ### Added diff --git a/drf_standardized_errors/formatter.py b/drf_standardized_errors/formatter.py index f51c374..32e9ecd 100644 --- a/drf_standardized_errors/formatter.py +++ b/drf_standardized_errors/formatter.py @@ -135,7 +135,7 @@ def flatten_errors( elif isinstance(detail, dict): for key, value in detail.items(): - if attr: + if attr is not None: key = f"{attr}{package_settings.NESTED_FIELD_SEPARATOR}{key}" fifo.append((value, key, None)) diff --git a/tests/test_flatten_errors.py b/tests/test_flatten_errors.py index 92d58c9..19fd7f1 100644 --- a/tests/test_flatten_errors.py +++ b/tests/test_flatten_errors.py @@ -126,3 +126,22 @@ def test_exception_with_detail_empty(): assert len(errors) == 1 assert errors[0].attr == "some_field" assert errors[0].detail == "" + + +@pytest.fixture +def list_serializer_errors_indexed_dict(): + # since DRF 3.18, ListSerializer.to_internal_value collects child errors in a + # dict keyed by the integer index of the item instead of a positional list. + return { + 0: {"email": [ErrorDetail("Enter a valid email address.", code="invalid")]}, + 1: {"email": [ErrorDetail("Enter a valid email address.", code="invalid")]}, + } + + +def test_list_serializer_errors_indexed_dict(list_serializer_errors_indexed_dict): + errors = flatten_errors(list_serializer_errors_indexed_dict) + assert len(errors) == 2 + assert errors[0].code == "invalid" + assert errors[0].attr == "0.email" + assert errors[1].code == "invalid" + assert errors[1].attr == "1.email"