Bêta privéeMailbeam est en bêta privée : l'API publique n'est pas encore ouverte.Rejoindre la liste d'attente

Mailbeam
Django + DRFDébutant15 minutesMis à jour en janvier 2025

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_email qui enveloppe l'appel à Mailbeam et échoue en mode permissif sur les erreurs d'API
  • Un validateur validate_email_deliverable pour 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 email

Django 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

PratiquePourquoi
Centraliser dans verification.pyFormulaires, sérialiseurs et vues restent cohérents
Échec permissif sur mailbeam.APIErrorUne panne ne doit pas casser les inscriptions
Normaliser (strip().lower())Cache cohérent et moins de recherches en double
Mettre en cache ~24 hRéduit les appels d'API sur les réessais et les resoumissions
Utiliser les domaines de testTests déterministes, sans consommation de quota

Liste de contrôle avant la mise en production

  • MAILBEAM_KEY chargé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_SCORE ajusté à votre tunnel
  • Message d'erreur avenant dans vos gabarits

Prochaines étapes