Skip to content

Fix UniqueConstraint validation with conditional fields (#9707) - #10021

Open
majidkhazaei wants to merge 13 commits into
encode:mainfrom
majidkhazaei:pr-9744
Open

Fix UniqueConstraint validation with conditional fields (#9707)#10021
majidkhazaei wants to merge 13 commits into
encode:mainfrom
majidkhazaei:pr-9744

Conversation

@majidkhazaei

Copy link
Copy Markdown

This is a rebased and conflict-resolved version of PR #9744.

Changes:

  • Added get_referenced_base_fields_from_q helper to rest_framework/compat.py
  • Fixed test indentation issues (moved test methods inside the correct class)
  • All 67 tests in tests/test_validators.py are passing

Resolves #9707
Supersedes #9744 (with resolved conflicts)

When using Django's UniqueConstraint with conditions that reference other fields,
DRF now correctly applies UniqueTogetherValidator instead of UniqueValidator.

nefrob and others added 4 commits August 13, 2026 16:08
- Add helper function for extracting fields from Q objects
- Move test methods inside TestUniquenessTogetherValidation class
- All 67 tests passing

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@auvipy
auvipy requested review from auvipy and a lite review from Copilot August 13, 2026 14:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes DRF’s ModelSerializer uniqueness validation for Django UniqueConstraint objects whose condition references additional model fields, ensuring DRF uses serializer-level UniqueTogetherValidator (with condition-awareness) instead of an incorrect field-level UniqueValidator.

Changes:

  • Add get_referenced_base_fields_from_q() compatibility helper and use it to detect fields referenced by UniqueConstraint.condition.
  • Update uniqueness validator selection so single-field constraints with distinct condition fields are validated via UniqueTogetherValidator.
  • Extend validator test coverage and document the UniqueConstraint-with-conditions behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rest_framework/compat.py Adds helper to extract referenced base fields from Q conditions.
rest_framework/utils/field_mapping.py Skips field-level UniqueValidator when condition references additional fields.
rest_framework/serializers.py Treats certain single-field conditional UniqueConstraints as “unique-together” for serializer-level validation.
tests/test_validators.py Adds/adjusts tests for conditional-field uniqueness behavior and expected validator placement.
docs/api-guide/validators.md Documents how DRF handles UniqueConstraint conditions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/api-guide/validators.md Outdated
]


## Updating nested serializers
Comment thread rest_framework/compat.py Outdated
@auvipy auvipy added this to the 3.18 milestone Aug 13, 2026
@peterthomassen

Copy link
Copy Markdown
Collaborator

I find the whole conditional uniqueness stuff confusing and don't have the conceptual clarity in my head atm to give this a solid review (I'm sorry).

@majidkhazaei

Copy link
Copy Markdown
Author

Hi @browniebroke, just a quick follow-up on this PR.

The checks have been green for a while, and the branch is up to date. Whenever you have a chance, I’d really appreciate a review. If there’s anything I can adjust or clarify, please let me know.

Thanks!

@auvipy
auvipy requested a balanced review from Copilot August 31, 2026 14:04
@auvipy
auvipy removed the request for review from peterthomassen August 31, 2026 14:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment thread rest_framework/serializers.py Outdated
Comment on lines +1476 to +1480
yield (
constraint.fields,
model._default_manager,
condition_fields,
constraint.condition,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@majidkhazaei was this resolved ?

yield (
constraint.fields,
model._default_manager,
condition_fields,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@majidkhazaei please crosscheck this and other open suggestions

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @auvipy!

Yes, I'm currently reviewing all the suggestions from Copilot. I'll go through them one by one and apply the necessary fixes (especially the ones regarding empty constraint.fields and condition field changes).

I'll push the updates later today and let you know once they're ready for another look.

Appreciate your patience and guidance!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this?

…t for custom messages, recheck condition field changes

- Add check for empty constraint.fields in get_unique_together_constraints
- Preserve original constraint object for custom violation messages/codes
- Extend UniqueTogetherValidator to recheck when condition fields change
- Add test for condition field change triggering revalidation

All 68 tests passing.
@majidkhazaei

Copy link
Copy Markdown
Author

Hi @auvipy and @browniebroke,

All three Copilot issues are now fixed:

  • ✅ Empty constraint.fields skipped (expression-based constraints)
  • ✅ Original constraint preserved for custom messages/codes
  • ✅ Condition field changes now trigger revalidation

A new test was added for the condition field scenario. All 68 tests are passing.

PR is ready for final review and merge. Thanks! 🙏

@auvipy

auvipy commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

tried to fix a newly emerged merge conflict

@majidkhazaei

Copy link
Copy Markdown
Author

All conflicts resolved, 76 tests passing, pre-commit checks green.
Ready for final review. Thanks!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Validation can be skipped for omitted or nullable condition fields, and the new transition test does not exercise the regression.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment on lines +217 to +223
checked_values = [attrs.get(field_name) for field_name in checked_names]
else:
# Ignore validation if all field values are unchanged
checked_values = [
attrs[field_name]
attrs.get(field_name)
for field_name in checked_names
if attrs[field_name] != getattr(serializer.instance, field_name)
if attrs.get(field_name) != getattr(serializer.instance, field_name, None)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@majidkhazaei please cross check this and other open suggestions

Comment thread tests/test_validators.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

Serializer UniqueConstraint validation fails incorrectly on create with conditional fields

5 participants