Vérification d'e-mails dans Django
Ce tutoriel montre comment ajouter la vérification d'e-mails en temps réel à un projet Django. Vous écrirez une seule fonction utilitaire réutilisable, que vous brancherez à la fois sur un formulaire Django classique et sur un sérialiseur Django REST Framework (DRF), de sorte que les adresses invalides, jetables et non distribuables soient rejetées avant la création du moindre utilisateur.
Ce que vous allez construire
- Une fonction
verify_emailqui enveloppe l'appel à Mailbeam et échoue en mode permissif sur les erreurs d'API - Un validateur
validate_email_deliverablepour les formulaires Django - Un validateur de champ de sérialiseur DRF pour les projets orientés API
Prérequis
- Python 3.9+ et Django 4.2 ou plus récent
- Une clé d'API Mailbeam (inscription gratuite)
python-dotenv(ou votre chargeur de réglages préféré)
Étape 1 — Ajouter un client HTTP
Il n'y a pas de SDK à installer. Mailbeam se résume à un endpoint HTTP : l'intégration ci-dessous constitue tout le client dont vous avez besoin. Des SDK officiels sont à la feuille de route.
pip install httpx # ou requests, s'il est déjà dans le projetÉtape 2 — Configurer les réglages
Gardez la clé hors du contrôle de version. Chargez-la depuis l'environnement dans settings.py :
# settings.py
import os
MAILBEAM_KEY = os.environ["MAILBEAM_KEY"]
# Score de qualité minimum accepté (0–100). 60 est un défaut raisonnable.
MAILBEAM_MIN_SCORE = int(os.environ.get("MAILBEAM_MIN_SCORE", "60"))# .env
MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxxÉtape 3 — Écrire une fonction utilitaire réutilisable
Rassemblez l'intégration en un seul endroit, pour que formulaires, sérialiseurs et vues partagent exactement le même comportement.
# accounts/verification.py
import logging
import httpx
from django.conf import settings
logger = logging.getLogger(__name__)
ENDPOINT = "https://api.mailbeam.dev/v1/verify"
def verify_email(email: str) -> tuple[bool, str | None]:
"""
Renvoie (is_acceptable, reason_code).
Échoue en mode PERMISSIF (renvoie True) sur une erreur de l'API Mailbeam,
pour qu'une panne ne bloque jamais une inscription légitime.
"""
try:
response = httpx.post(
ENDPOINT,
json={"email": email.strip().lower()},
headers={"Authorization": f"Bearer {settings.MAILBEAM_KEY}"},
timeout=3.0,
)
response.raise_for_status()
result = response.json()
except httpx.HTTPError as exc:
logger.error("Échec de la vérification Mailbeam : %s", exc)
return True, None # échec permissif
if not result["valid"] or result["score"] < settings.MAILBEAM_MIN_SCORE:
return False, result["reason"] or "invalid_email"
return True, NoneÉtape 4 — L'utiliser dans un formulaire Django
# accounts/forms.py
from django import forms
from django.core.exceptions import ValidationError
from .verification import verify_email
class SignupForm(forms.Form):
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
def clean_email(self):
email = self.cleaned_data["email"]
ok, reason = verify_email(email)
if not ok:
raise ValidationError(
"Merci de saisir une adresse e-mail valide et distribuable.",
code=reason,
)
return emailDjango appelle clean_email automatiquement pendant form.is_valid(), donc votre vue n'a pas besoin de changer :
# accounts/views.py
from django.shortcuts import render, redirect
from .forms import SignupForm
def signup(request):
if request.method == "POST":
form = SignupForm(request.POST)
if form.is_valid():
create_user(email=form.cleaned_data["email"])
return redirect("welcome")
else:
form = SignupForm()
return render(request, "accounts/signup.html", {"form": form})Étape 5 — L'utiliser dans un sérialiseur DRF
Pour un projet orienté API, validez plutôt dans le sérialiseur :
# accounts/serializers.py
from rest_framework import serializers
from .verification import verify_email
class SignupSerializer(serializers.Serializer):
email = serializers.EmailField()
password = serializers.CharField(write_only=True)
def validate_email(self, value):
ok, reason = verify_email(value)
if not ok:
raise serializers.ValidationError(
f"Adresse refusée : {reason}"
)
return value# accounts/views.py (DRF)
from rest_framework.generics import CreateAPIView
from .serializers import SignupSerializer
class SignupView(CreateAPIView):
serializer_class = SignupSerializer
# DRF renvoie automatiquement un 400 avec les erreurs de champ si la validation échoueÉtape 6 — Ajouter un cache
Évitez de revérifier la même adresse (par exemple quand un utilisateur resoumet un formulaire). Le framework de cache de Django s'y prête bien :
# accounts/verification.py
from django.core.cache import cache
def verify_email(email: str) -> tuple[bool, str | None]:
normalized = email.strip().lower()
cached = cache.get(f"mb:{normalized}")
if cached is not None:
return cached
try:
result = _client.verify_sync(normalized)
ok = result.valid and result.score >= settings.MAILBEAM_MIN_SCORE
outcome = (ok, None if ok else (result.reason or "invalid_email"))
cache.set(f"mb:{normalized}", outcome, timeout=86_400) # 24 h
return outcome
except mailbeam.APIError as exc:
logger.error("Échec de la vérification Mailbeam : %s", exc)
return True, None # échec permissif (on ne met pas les échecs en cache)Tester
Les domaines de test de Mailbeam renvoient des résultats déterministes et n'entament pas votre quota :
# accounts/tests.py
from django.test import TestCase
from .forms import SignupForm
class SignupFormTests(TestCase):
def test_valid_email_passes(self):
form = SignupForm(data={
"email": "user@valid.mailbeam-test.dev",
"password": "pass1234",
})
self.assertTrue(form.is_valid())
def test_disposable_email_rejected(self):
form = SignupForm(data={
"email": "temp@disposable.mailbeam-test.dev",
"password": "pass1234",
})
self.assertFalse(form.is_valid())
self.assertIn("email", form.errors)Bonnes pratiques
| Pratique | Pourquoi |
|---|---|
Centraliser dans verification.py | Formulaires, sérialiseurs et vues restent cohérents |
Échec permissif sur mailbeam.APIError | Une panne ne doit pas casser les inscriptions |
Normaliser (strip().lower()) | Cache cohérent et moins de recherches en double |
| Mettre en cache ~24 h | Réduit les appels d'API sur les réessais et les resoumissions |
| Utiliser les domaines de test | Tests déterministes, sans consommation de quota |
Liste de contrôle avant la mise en production
-
MAILBEAM_KEYchargée depuis l'environnement, jamais versionnée - Journalisation configurée pour
mailbeam.APIError - Backend de cache défini (Redis ou Memcached en production)
-
MAILBEAM_MIN_SCOREajusté à votre tunnel - Message d'erreur avenant dans vos gabarits