Skip to content

fix(blank/unique): add default handling for blank=True field#9751

Open
sshishov wants to merge 6 commits intoencode:mainfrom
sshishov:ss/fix/allow-blank-unique-constraint
Open

fix(blank/unique): add default handling for blank=True field#9751
sshishov wants to merge 6 commits intoencode:mainfrom
sshishov:ss/fix/allow-blank-unique-constraint

Conversation

@sshishov
Copy link
Copy Markdown
Contributor

@sshishov sshishov commented Aug 2, 2025

Description

This MR should fix the issue with blank=True field in UniqueTogether constraint. It should not become required all of a sudden.

Fixes #9750

Signed-off-by: Sergei Shishov <sshishov.sshishov@gmail.com>
@sshishov sshishov force-pushed the ss/fix/allow-blank-unique-constraint branch from 3bcd191 to 8de6a6c Compare August 2, 2025 12:56
@sshishov
Copy link
Copy Markdown
Contributor Author

sshishov commented Aug 2, 2025

I have added tests similar to the tests added in #9531

Copy link
Copy Markdown
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

did you check this pr #9725 ?


def test_blank_uqnique_constraint_fields_are_not_required(self):
serializer = UniqueConstraintBlankSerializer(data={'age': 25})
self.assertTrue(serializer.is_valid(), serializer.errors)
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.

Can we split it into multiple assertions?

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 an issue where blank=True fields in unique_together constraints were incorrectly becoming required. The fix adds special handling for blank fields by setting their default value to an empty string, similar to how null=True fields default to None. This ensures that blank fields in uniqueness constraints remain optional while still properly validating uniqueness.

Key Changes

  • Added default='' handling for blank fields in unique constraints in get_uniqueness_extra_kwargs
  • Added comprehensive test coverage for both unique_together and UniqueConstraint with blank fields
  • Tests verify that blank fields are not required, while still enforcing uniqueness validation

Reviewed changes

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

File Description
rest_framework/serializers.py Added conditional logic to set default='' for blank fields in the uniqueness constraint handling, following the same pattern as null field handling
tests/test_validators.py Added new test models (BlankUniquenessTogetherModel, UniqueConstraintBlankModel) and test cases to verify blank fields in uniqueness constraints are not required but still validate correctly

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +152 to +158
class BlankUniquenessTogetherModel(models.Model):
race_name = models.CharField(max_length=100, blank=True)
position = models.IntegerField()

class Meta:
unique_together = ('race_name', 'position')

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

Consider adding a docstring to BlankUniquenessTogetherModel similar to NullUniquenessTogetherModel (lines 160-172) to explain the purpose and expected behavior of blank fields in unique_together constraints.

Copilot uses AI. Check for mistakes.
Comment on lines +489 to +505
def test_validation_for_missing_blank_fields(self):
BlankUniquenessTogetherModel.objects.create(
position=1
)
data = {
'position': 1
}
serializer = BlankUniquenessTogetherSerializer(data=data)
assert not serializer.is_valid()

def test_ignore_validation_for_missing_blank_fields(self):
data = {
'position': 1
}
serializer = BlankUniquenessTogetherSerializer(data=data)
assert serializer.is_valid(), serializer.errors

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The test names test_validation_for_missing_blank_fields and test_ignore_validation_for_missing_blank_fields are confusing because both refer to "missing blank fields" but have opposite expectations. Consider renaming to make the distinction clearer, such as test_validation_fails_for_duplicate_blank_defaults and test_validation_passes_when_no_duplicates.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

please also fix the merge conflicts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@encode encode deleted a comment from mahdighadiriii Apr 2, 2026
@encode encode deleted a comment from stale bot Apr 2, 2026
auvipy and others added 3 commits April 2, 2026 14:07
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

tests/test_validators.py:831

  • This test module consistently uses plain assert statements; these new tests use self.assertTrue / self.assertIsInstance instead. For consistency and readability, switch these to assert serializer.is_valid(), serializer.errors and assert isinstance(result, ...) like the rest of the file.
    def test_blank_unique_constraint_fields_are_not_required(self):
        serializer = UniqueConstraintBlankSerializer(data={'age': 25})
        self.assertTrue(serializer.is_valid(), serializer.errors)
        result = serializer.save()
        self.assertIsInstance(result, UniqueConstraintBlankModel)

    def test_nullable_unique_constraint_fields_are_not_required(self):
        serializer = UniqueConstraintNullableSerializer(data={'title': 'Bob'})
        self.assertTrue(serializer.is_valid(), serializer.errors)
        result = serializer.save()
        self.assertIsInstance(result, UniqueConstraintNullableModel)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

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

let see if tests passes now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3.15 is raising required error on model blank fields

5 participants