Verifica delle email in Django
Questo tutorial mostra come aggiungere la verifica delle email in tempo reale a un progetto Django. Costruirai un helper riutilizzabile e lo collegherai sia a un modulo classico di Django sia a un serializer di Django REST Framework (DRF), così che gli indirizzi non validi, usa e getta e non recapitabili vengano rifiutati prima che venga creato qualsiasi utente.
Che cosa costruirai
- Un helper
verify_emailche avvolge l'API di Mailbeam e in caso di errore lascia passare - Un validatore
validate_email_deliverableper i moduli di Django - Un validatore di campo di serializer di DRF per i progetti orientati alle API
Prerequisiti
- Python 3.9+ e Django 4.2 o successivo
- Una chiave API di Mailbeam (iscriviti gratis)
python-dotenv(o il caricatore di settings che preferisci)
Passo 1 — Aggiungi un client HTTP
Non c'è nessun SDK da installare. Mailbeam è un unico endpoint HTTP, quindi l'integrazione qui sotto è tutto il client che ti serve. Gli SDK ufficiali sono nella roadmap.
pip install httpx # oppure usa requests, se è già nel progettoPasso 2 — Configura i settings
Tieni la chiave fuori dal controllo di versione. Caricala dall'ambiente in settings.py:
# settings.py
import os
MAILBEAM_KEY = os.environ["MAILBEAM_KEY"]
# Punteggio minimo di qualità per accettare (0-100). 60 è un valore ragionevole.
MAILBEAM_MIN_SCORE = int(os.environ.get("MAILBEAM_MIN_SCORE", "60"))# .env
MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxxPasso 3 — Scrivi un helper di verifica riutilizzabile
Metti l'integrazione in un punto solo, così moduli, serializer e viste condividono lo stesso comportamento.
# 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]:
"""
Restituisce (è_accettabile, codice_del_motivo).
In caso di errore dell'API di Mailbeam LASCIA PASSARE (restituisce True),
così un guasto non blocca mai registrazioni legittime.
"""
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("Verifica di Mailbeam fallita: %s", exc)
return True, None # lascia passare
if not result["valid"] or result["score"] < settings.MAILBEAM_MIN_SCORE:
return False, result["reason"] or "invalid_email"
return True, NonePasso 4 — Usalo in un modulo di 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(
"Inserisci un indirizzo email valido e recapitabile.",
code=reason,
)
return emailDjango esegue clean_email automaticamente durante form.is_valid(), quindi la tua vista non cambia:
# 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})Passo 5 — Usalo in un serializer di DRF
Nei progetti orientati alle API, valida dentro il serializer:
# 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"Email rifiutata: {reason}"
)
return value# accounts/views.py (DRF)
from rest_framework.generics import CreateAPIView
from .serializers import SignupSerializer
class SignupView(CreateAPIView):
serializer_class = SignupSerializer
# DRF restituisce automaticamente un 400 con gli errori di campo se la validazione falliscePasso 6 — Aggiungi la cache
Evita di riverificare lo stesso indirizzo (per esempio quando qualcuno riprova il modulo). Il framework di cache di Django ci sta bene:
# 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("Verifica di Mailbeam fallita: %s", exc)
return True, None # lascia passare (non mettere in cache i fallimenti)Prove
I domini di prova di Mailbeam restituiscono risultati deterministici e non consumano 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)Buone pratiche
| Pratica | Perché |
|---|---|
Centralizzare in verification.py | Moduli, serializer e viste si comportano allo stesso modo |
Lasciar passare davanti a mailbeam.APIError | Un guasto non deve rompere le registrazioni |
Normalizzare (strip().lower()) | Cache coerente e meno interrogazioni doppie |
| Mettere in cache ~24 h | Riduce le chiamate all'API nei ritentativi e nei reinvii |
| Usare i domini di prova | Test deterministici e senza consumo di quota |
Lista di controllo per la produzione
-
MAILBEAM_KEYcaricata dall'ambiente, non caricata nel repository - Registrazione degli errori configurata per
mailbeam.APIError - Backend di cache definito (Redis o Memcached in produzione)
-
MAILBEAM_MIN_SCOREregolato sul tuo imbuto - Il messaggio di errore che vede l'utente è gentile nei tuoi modelli