﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
34319	ValidationError handling during model.validate_constraints	Mateusz Kurowski	nobody	"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):
    ...

}}}

"	Cleanup/optimization	new	Database layer (models, ORM)	4.1	Normal		Model, validate_constraints, ValidationError, code, message		Unreviewed	0	0	0	0	0	0
