#36809: Allow EmailValidator to customize error messages depending on the part
that
failed validation
-------------------------------------+-------------------------------------
Reporter: Daniel E Onetti | Owner: jaffar
Type: | Khan
Cleanup/optimization | Status: assigned
Component: Core (Other) | Version: dev
Severity: Normal | Resolution:
Keywords: EmailValidator | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1
Needs tests: 1 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Mike Edmunds):
Natalia, in attempting to review jaffar Khan's PR, I realized that while I
agree with these statements in theory, I'm having trouble putting them
into practice:
Replying to [comment:3 Natalia Bidart]:
> … Looking at the other validators in this module, there is a clear
pattern of separating parsing, normalization, and validation into distinct
steps or methods, even when the final error surface remains simple.
`EmailValidator` is somewhat inconsistent here: it already decomposes the
value into user and domain components, but does so inline inside
`__call__`, with no clear extension points. Adding a single extra param
addresses the symptom rather than the structure.
>
> I think a more Django aligned approach would be to re-evaluate
`EmailValidator` as a whole and give it clearer, overridable validation
hooks, while keeping full backward compatibility. For example, introducing
explicit methods like `validate_recipient()` and `validate_domain()`,
called from `__call__`, would allow customization via subclassing without
re-parsing the email or wrapping errors. …
I couldn't tell which validators you were referring to that separate
parsing, normalization and validation. EmailValidator follows the pattern
of the RegexValidators: it has some (overridable) regex and list
properties, and it does pretty much ''everything'' in its `__call__()`
method. Unlike the RegexValidators, it also has one extension point
method: `validate_domain_part()` allows replacing the domain validation
logic (but not `domain_allowlist` exceptions or ValidationError
construction). I ''believe'' the intent was to support stricter domain
validation via subclassing (e.g., for users who felt DomainNameValidator
was too lax or wanted to disallow numeric domain literals).
If a refactored EmailValidator looks something like:
{{{#!python
class EmailValidator:
def __call__(self, value):
... # pre-validation omitted
username, domain = value.rsplit("@", 1) # or
self.parse_parts(value) ?
self.validate_username(username, value)
self.validate_domain(domain, value)
def validate_domain(self, domain, value):
if domain not in self.domain_allowlist and not
some_other_logic_on(domain):
raise ValidationError(self.message, code=self.code,
params={"value": value})
}}}
… then the original ticket request to have access to the domain in the
error message ''does'' seem to require wrapping errors (or duplicating
logic from the superclass):
{{{#!python
class EmailValidatorWithBetterErrorMessages(EmailValidator):
def validate_domain(self, domain, value):
try:
super().validate_domain(domain, value)
except ValidationError as error:
# change "code" and add domain to the error params
raise ValidationError(self.message, code="invalid-domain",
params={"value": value, "domain": domain}) from error
}}}
… and substituting the domain validation logic requires duplicating some
of the superclass code:
{{{#!python
class EmailValidatorWithCustomDomainValidation(EmailValidator):
def validate_domain(self, domain, value):
if domain in self.domain_allowlist:
return # duplicate allowlist logic from superclass
if not idna.utils.is_valid_domain(domain):
raise ValidationError(self.message, code=self.code,
params={"value": value})
}}}
… which makes me think I've misunderstood what you were envisioning in
reworking EmailValidator to improve subclassing hooks.
--
Ticket URL: <https://code.djangoproject.com/ticket/36809#comment:12>
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 visit
https://groups.google.com/d/msgid/django-updates/0107019c15c32954-1884af25-7ba3-439e-9a6a-95ed4a2d0fb7-000000%40eu-central-1.amazonses.com.