An unsupported path failed through an unrelated internal attribute.
Django REST Framework supports bulk input through serializers configured with many=True, but multiple update behavior requires application-specific identity and update logic. In the scenario covered by issue #6010, a custom ListSerializer received a queryset instance while its child ModelSerializer had an automatically generated UniqueTogetherValidator.
PR #9981, authored by Zain Nadeem, does not add general multiple-update support. It replaces the unclear internal failure with a deliberate error explaining what a custom list serializer must establish before child validation.
The validator expected one model instance but received a queryset.
During single-instance update validation, UniqueTogetherValidator excludes the object being updated from its uniqueness query by reading the current instance’s primary key. In the unsupported bulk path, the child serializer could inherit the parent queryset as serializer.instance. The validator then attempted to access .pk on that queryset and raised:
AttributeError: 'QuerySet' object has no attribute 'pk'That exception exposed an implementation assumption without telling developers how to structure a supported custom update flow.
Child-instance identity was missing before validation ran.
A uniqueness validator needs the concrete current object so that unchanged values do not conflict with the same database row. DRF cannot infer that mapping for arbitrary multiple updates. The custom ListSerializer must associate each incoming record with the correct model instance before running child validation.
Detect the invalid state before queryset filtering.
The patch adds a narrow guard at the start of UniqueTogetherValidator.__call__(). It activates when the serializer has an instance, its parent is operating with many=True, and that instance lacks a pk attribute. The resulting RuntimeError directs implementers to override ListSerializer.run_child_validation() and set child.instance first.
The validator remains enabled. The patch intentionally avoids silently treating the operation as create validation, which could change uniqueness semantics and hide an invalid update configuration.
Reproduce the queryset-backed many-update path.
The regression defines a model with a compound uniqueness constraint, a serializer with writable identity, and a custom list serializer. It passes a queryset as the instance with many=True, then asserts that validation raises the new explanatory error. The PR record also documents focused validator and serializer-list test runs.
The explicit contract was merged without changing normal validation.
Zain authored PR #9981. It changed rest_framework/validators.py and added regression coverage in tests/test_validators.py. The patch was merged on 6 August 2026 as commit 7508e0cf122d5250a2cba824a3c537a288acb489. Normal create validation, single-instance update validation, and custom implementations that correctly assign child instances remain outside the guard.
Unsupported workflows should fail at the violated contract.
- Validate preconditions before deeper code fails on incidental attributes.
- Bulk updates require an explicit identity mapping; frameworks should not guess it.
- Keeping a validator active is safer than silently changing update semantics to create semantics.
- Actionable errors should identify both the missing state and the extension point that supplies it.