Opened 22 months ago
Last modified 22 months ago
#34319 closed Bug
ValidationError handling during model.validate_constraints — at Version 4
Reported by: | Mateusz Kurowski | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 4.1 |
Severity: | Release blocker | Keywords: | Model, validate_constraints, ValidationError, code, message |
Cc: | Gagaro | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Imagine scenario when i want to explicitly mark a field that model constraint should raise ValidationError for:
class CustomUniqueConstraint(UniqueConstraint): def validate(self, *args, **kwargs): try: value = super().validate(*args, **kwargs) except ValidationError as e: raise ValidationError( { 'email': e, } ) return value class AbstractUser(django.contrib.auth.models.AbstractUser): class Meta: abstract = True constraints = [ CustomUniqueConstraint( Lower("email"), name="%(app_label)s_%(class)s_email_unique", ) ]
This wont work because:
1425, in validate_constraints if e.code == "unique" and len(constraint.fields) == 1: ^^^^^^ AttributeError: 'ValidationError' object has no attribute 'code'
Maybe all unique constraints should allow raising validation error for specific field like ?
from django.core.exceptions import ValidationError from django.db import models class ViolationFieldNameMixin: """ Mixin for BaseConstraint subclasses that builds custom ValidationError message for the `violation_field_name`. By this way we can bind the error to the field that caused it. This is useful in ModelForms where we can display the error message next to the field and also avoid displaying unique constraint violation error messages more than once for the same field. """ def __init__(self, *args, **kwargs): self.violation_field_name = kwargs.pop("violation_field_name", None) self.violation_code = kwargs.pop("violation_code", None) super().__init__(*args, **kwargs) def validate(self, *args, **kwargs): try: value = super().validate(*args, **kwargs) except ValidationError as e: e.code = self.violation_code # Create a new ValidationError with the violation_field_name attribute as the key e = ValidationError({self.violation_field_name: e}) # Set the error code to None # See https://code.djangoproject.com/ticket/34319#ticket e.code = self.violation_code raise e return value def deconstruct(self): path, args, kwargs = super().deconstruct() kwargs["violation_field_name"] = self.violation_field_name kwargs["violation_code"] = self.violation_code return path, args, kwargs def __eq__(self, other): return ( super().__eq__(other) and self.violation_field_name == getattr(other, "violation_field_name", None) and self.violation_code == getattr(other, "violation_code", None) ) class UniqueConstraint(ViolationFieldNameMixin, models.UniqueConstraint): ...
Change History (3)
comment:2 by , 22 months ago
Description: | modified (diff) |
---|---|
Summary: | Model.validate_constraints check for ValidationError code → ValidationError handling during model.validate_constraints |
Type: | Bug → Cleanup/optimization |
comment:3 by , 22 months ago
Description: | modified (diff) |
---|
comment:4 by , 22 months ago
Description: | modified (diff) |
---|
Note:
See TracTickets
for help on using tickets.