#34319: ValidationError handling during model.validate_constraints
-------------------------------------+-------------------------------------
     Reporter:  Mateusz Kurowski     |                    Owner:  nobody
         Type:                       |                   Status:  new
  Cleanup/optimization               |
    Component:  Database layer       |                  Version:  4.1
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  Model,               |             Triage Stage:
  validate_constraints,              |  Unreviewed
  ValidationError, code, message     |
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Mateusz Kurowski:

Old description:

> 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'
> }}}

New description:

 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:
             # 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):
     ...

 }}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34319#comment:3>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/010701862dfd951d-1f3e0d3c-0852-44f6-8df4-52b9415047be-000000%40eu-central-1.amazonses.com.

Reply via email to