Django
Email checker for Django. Python email verification in Django views and forms.
Integre EmailVerify con aplicaciones Django usando validadores personalizados, campos de formulario y middleware.
Instalación
pip install emailverifyConfiguración
Agregue a su configuración de Django:
# settings.py
EMAILVERIFY_API_KEY = os.environ.get('EMAILVERIFY_API_KEY')
# Configuraciones opcionales
EMAILVERIFY_TIMEOUT = 10 # segundos
EMAILVERIFY_BLOCK_DISPOSABLE = True
EMAILVERIFY_BLOCK_ROLE_BASED = False
EMAILVERIFY_CACHE_DURATION = 3600 # 1 horaValidador Personalizado
Cree un validador de correo electrónico reutilizable.
# validators.py
from django.core.exceptions import ValidationError
from django.conf import settings
from emailverify import Client
import logging
logger = logging.getLogger(__name__)
def validate_email_deliverable(email):
"""Validar que una dirección de correo electrónico sea entregable."""
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(email)
if result.status == 'invalid':
raise ValidationError(
'Por favor, ingrese una dirección de correo electrónico válida.',
code='invalid_email'
)
if getattr(settings, 'EMAILVERIFY_BLOCK_DISPOSABLE', True):
if result.result.disposable:
raise ValidationError(
'No se permiten direcciones de correo electrónico desechables.',
code='disposable_email'
)
if getattr(settings, 'EMAILVERIFY_BLOCK_ROLE_BASED', False):
if result.result.role:
raise ValidationError(
'No se permiten direcciones de correo electrónico basadas en roles.',
code='role_email'
)
except Exception as e:
# Registrar error pero no bloquear el envío
logger.warning(f'La verificación de correo electrónico falló para {email}: {e}')
class EmailDeliverableValidator:
"""Validador basado en clases con opciones personalizables."""
def __init__(self, block_disposable=True, block_role_based=False):
self.block_disposable = block_disposable
self.block_role_based = block_role_based
self.client = Client(api_key=settings.EMAILVERIFY_API_KEY)
def __call__(self, email):
try:
result = self.client.verify(email)
if result.status == 'invalid':
raise ValidationError(
'Por favor, ingrese una dirección de correo electrónico válida.',
code='invalid_email'
)
if self.block_disposable and result.result.disposable:
raise ValidationError(
'No se permiten direcciones de correo electrónico desechables.',
code='disposable_email'
)
if self.block_role_based and result.result.role:
raise ValidationError(
'No se permiten direcciones de correo electrónico basadas en roles.',
code='role_email'
)
except ValidationError:
raise
except Exception as e:
logger.warning(f'La verificación de correo electrónico falló: {e}')
def __eq__(self, other):
return (
isinstance(other, EmailDeliverableValidator) and
self.block_disposable == other.block_disposable and
self.block_role_based == other.block_role_based
)Campo de Modelo
Agregue verificación a campos de modelo.
# models.py
from django.db import models
from .validators import validate_email_deliverable
class User(models.Model):
email = models.EmailField(
unique=True,
validators=[validate_email_deliverable]
)
name = models.CharField(max_length=255)
email_verification_status = models.CharField(
max_length=20,
blank=True,
null=True
)
email_verification_score = models.FloatField(
blank=True,
null=True
)
email_verified_at = models.DateTimeField(
blank=True,
null=True
)
class Meta:
db_table = 'users'Integración con Formularios
Formulario Básico
# forms.py
from django import forms
from django.core.validators import EmailValidator
from .validators import EmailDeliverableValidator
class RegistrationForm(forms.Form):
name = forms.CharField(max_length=255)
email = forms.EmailField(
validators=[
EmailValidator(),
EmailDeliverableValidator(
block_disposable=True,
block_role_based=False
)
]
)
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
password_confirm = cleaned_data.get('password_confirm')
if password and password_confirm and password != password_confirm:
raise forms.ValidationError('Las contraseñas no coinciden.')
return cleaned_dataFormulario de Modelo
# forms.py
from django import forms
from .models import User
from .validators import validate_email_deliverable
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ['name', 'email']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Agregar validador al campo de correo electrónico
self.fields['email'].validators.append(validate_email_deliverable)Integración con Vistas
# views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.views import View
from emailverify import Client
from django.conf import settings
from .forms import RegistrationForm
from .models import User
class RegistrationView(View):
template_name = 'registration/register.html'
def get(self, request):
form = RegistrationForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = RegistrationForm(request.POST)
if form.is_valid():
# Verificación adicional con resultado completo
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
email = form.cleaned_data['email']
try:
result = client.verify(email)
# Crear usuario con datos de verificación
user = User.objects.create(
name=form.cleaned_data['name'],
email=email,
email_verification_status=result.status,
email_verification_score=result.score,
)
user.set_password(form.cleaned_data['password'])
user.save()
messages.success(request, '¡Registro exitoso!')
return redirect('login')
except Exception as e:
messages.error(request, 'El registro falló. Por favor, inténtelo de nuevo.')
return render(request, self.template_name, {'form': form})Middleware
Verifique correos electrónicos en solicitudes entrantes.
# middleware.py
from django.http import JsonResponse
from django.conf import settings
from emailverify import Client
import logging
import json
logger = logging.getLogger(__name__)
class EmailVerificationMiddleware:
"""Middleware para verificar direcciones de correo electrónico en solicitudes POST."""
def __init__(self, get_response):
self.get_response = get_response
self.client = Client(api_key=settings.EMAILVERIFY_API_KEY)
self.protected_paths = getattr(
settings,
'EMAILVERIFY_PROTECTED_PATHS',
['/api/register/', '/api/subscribe/']
)
def __call__(self, request):
if request.method == 'POST' and self._should_verify(request.path):
email = self._extract_email(request)
if email:
try:
result = self.client.verify(email)
if result.status == 'invalid':
return JsonResponse(
{'error': 'Dirección de correo electrónico inválida'},
status=400
)
# Adjuntar resultado de verificación a la solicitud
request.email_verification = result
except Exception as e:
logger.warning(f'La verificación de correo electrónico falló: {e}')
return self.get_response(request)
def _should_verify(self, path):
return any(path.startswith(p) for p in self.protected_paths)
def _extract_email(self, request):
# Intentar cuerpo JSON
if request.content_type == 'application/json':
try:
data = json.loads(request.body)
return data.get('email')
except json.JSONDecodeError:
pass
# Intentar datos POST
return request.POST.get('email')Registrar Middleware
# settings.py
MIDDLEWARE = [
# ... otro middleware
'myapp.middleware.EmailVerificationMiddleware',
]
EMAILVERIFY_PROTECTED_PATHS = [
'/api/register/',
'/api/subscribe/',
'/api/contact/',
]Almacenamiento en Caché
Almacene en caché los resultados de verificación para reducir las llamadas a la API.
# services.py
from django.core.cache import cache
from django.conf import settings
from emailverify import Client
import hashlib
class EmailVerificationService:
def __init__(self):
self.client = Client(api_key=settings.EMAILVERIFY_API_KEY)
self.cache_duration = getattr(
settings,
'EMAILVERIFY_CACHE_DURATION',
3600
)
def verify(self, email):
cache_key = self._get_cache_key(email)
cached = cache.get(cache_key)
if cached:
return cached
result = self.client.verify(email)
cache.set(cache_key, result, self.cache_duration)
return result
def is_valid(self, email):
result = self.verify(email)
return result.status == 'valid'
def is_disposable(self, email):
result = self.verify(email)
return result.result.disposable
def clear_cache(self, email):
cache.delete(self._get_cache_key(email))
def _get_cache_key(self, email):
email_hash = hashlib.md5(email.lower().encode()).hexdigest()
return f'email_verification:{email_hash}'
# Uso
verification_service = EmailVerificationService()
if verification_service.is_valid(email):
# Procesar correo electrónico válido
passDjango REST Framework
Validación de Serializador
# serializers.py
from rest_framework import serializers
from django.conf import settings
from emailverify import Client
class RegistrationSerializer(serializers.Serializer):
name = serializers.CharField(max_length=255)
email = serializers.EmailField()
password = serializers.CharField(write_only=True)
def validate_email(self, value):
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(value)
if result.status == 'invalid':
raise serializers.ValidationError(
'Por favor, ingrese una dirección de correo electrónico válida.'
)
if result.result.disposable:
raise serializers.ValidationError(
'No se permiten correos electrónicos desechables.'
)
# Almacenar resultado para uso posterior
self.context['email_verification'] = result
except serializers.ValidationError:
raise
except Exception as e:
# Registrar pero no bloquear
pass
return valuePermiso Personalizado
# permissions.py
from rest_framework.permissions import BasePermission
from django.conf import settings
from emailverify import Client
class ValidEmailPermission(BasePermission):
"""Solo permitir solicitudes con direcciones de correo electrónico válidas."""
message = 'Se requiere una dirección de correo electrónico válida.'
def has_permission(self, request, view):
email = request.data.get('email')
if not email:
return True # Dejar que otros validadores manejen el correo electrónico faltante
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(email)
return result.status != 'invalid'
except Exception:
return True # Permitir en caso de errores de APIComando de Administración
Verificar correos electrónicos de usuarios en masa.
# management/commands/verify_emails.py
from django.core.management.base import BaseCommand
from django.conf import settings
from emailverify import Client
from myapp.models import User
import time
class Command(BaseCommand):
help = 'Verificar todas las direcciones de correo electrónico de usuarios'
def add_arguments(self, parser):
parser.add_argument(
'--batch-size',
type=int,
default=100,
help='Número de correos electrónicos por lote'
)
def handle(self, *args, **options):
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
batch_size = options['batch_size']
users = User.objects.filter(
email_verification_status__isnull=True
)
total = users.count()
self.stdout.write(f'Verificando {total} correos electrónicos...')
# Recopilar correos electrónicos para verificación en masa
emails = list(users.values_list('email', flat=True)[:10000])
# Enviar trabajo en masa
job = client.verify_bulk(emails)
self.stdout.write(f'ID del trabajo: {job.job_id}')
# Esperar finalización
while True:
status = client.get_bulk_job_status(job.job_id)
self.stdout.write(
f'Progreso: {status.progress_percent}%',
ending='\r'
)
if status.status == 'completed':
break
time.sleep(5)
self.stdout.write('')
# Obtener y procesar resultados
results = client.get_bulk_job_results(job.job_id)
for result in results.results:
User.objects.filter(email=result.email).update(
email_verification_status=result.status,
email_verification_score=result.score,
)
self.stdout.write(self.style.SUCCESS('¡Verificación completa!'))
# Imprimir resumen
summary = {}
for result in results.results:
summary[result.status] = summary.get(result.status, 0) + 1
for status, count in summary.items():
self.stdout.write(f' {status}: {count}')Señales
Reaccionar a eventos de modelo.
# signals.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.conf import settings
from emailverify import Client
from .models import User
import logging
logger = logging.getLogger(__name__)
@receiver(pre_save, sender=User)
def verify_user_email(sender, instance, **kwargs):
"""Verificar correo electrónico antes de guardar usuario."""
# Omitir si el correo electrónico no ha cambiado
if instance.pk:
try:
old_instance = User.objects.get(pk=instance.pk)
if old_instance.email == instance.email:
return
except User.DoesNotExist:
pass
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(instance.email)
instance.email_verification_status = result.status
instance.email_verification_score = result.score
except Exception as e:
logger.warning(f'La verificación de correo electrónico falló: {e}')Pruebas
# tests.py
from django.test import TestCase
from unittest.mock import patch, MagicMock
from .validators import validate_email_deliverable
from django.core.exceptions import ValidationError
class EmailValidationTest(TestCase):
@patch('validators.Client')
def test_valid_email_passes(self, mock_client):
mock_result = MagicMock()
mock_result.status = 'valid'
mock_result.result.disposable = False
mock_result.result.role = False
mock_client.return_value.verify.return_value = mock_result
# No debería generar excepción
validate_email_deliverable('valid@example.com')
@patch('validators.Client')
def test_invalid_email_fails(self, mock_client):
mock_result = MagicMock()
mock_result.status = 'invalid'
mock_client.return_value.verify.return_value = mock_result
with self.assertRaises(ValidationError):
validate_email_deliverable('invalid@example.com')
@patch('validators.Client')
def test_disposable_email_blocked(self, mock_client):
mock_result = MagicMock()
mock_result.status = 'valid'
mock_result.result.disposable = True
mock_client.return_value.verify.return_value = mock_result
with self.assertRaises(ValidationError):
validate_email_deliverable('temp@mailinator.com')