Django Email Verification
Email checker for Django. Python email verification in Django views and forms.
Интегрируйте EmailVerify с Django приложениями, используя пользовательские валидаторы, поля форм и middleware.
Установка
pip install emailverifyНастройка
Добавьте в настройки Django:
# settings.py
EMAILVERIFY_API_KEY = os.environ.get('EMAILVERIFY_API_KEY')
# Опциональные настройки
EMAILVERIFY_TIMEOUT = 10 # секунды
EMAILVERIFY_BLOCK_DISPOSABLE = True
EMAILVERIFY_BLOCK_ROLE_BASED = False
EMAILVERIFY_CACHE_DURATION = 3600 # 1 часПользовательский валидатор
Создайте переиспользуемый валидатор email.
# 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):
"""Проверяет, что email-адрес доставляем."""
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(email)
if result.status == 'invalid':
raise ValidationError(
'Пожалуйста, введите корректный email-адрес.',
code='invalid_email'
)
if getattr(settings, 'EMAILVERIFY_BLOCK_DISPOSABLE', True):
if result.result.disposable:
raise ValidationError(
'Одноразовые email-адреса не допускаются.',
code='disposable_email'
)
if getattr(settings, 'EMAILVERIFY_BLOCK_ROLE_BASED', False):
if result.result.role:
raise ValidationError(
'Ролевые email-адреса не допускаются.',
code='role_email'
)
except Exception as e:
# Логируем ошибку, но не блокируем отправку
logger.warning(f'Ошибка верификации email {email}: {e}')
class EmailDeliverableValidator:
"""Классовый валидатор с настраиваемыми опциями."""
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(
'Пожалуйста, введите корректный email-адрес.',
code='invalid_email'
)
if self.block_disposable and result.result.disposable:
raise ValidationError(
'Одноразовые email-адреса не допускаются.',
code='disposable_email'
)
if self.block_role_based and result.result.role:
raise ValidationError(
'Ролевые email-адреса не допускаются.',
code='role_email'
)
except ValidationError:
raise
except Exception as e:
logger.warning(f'Ошибка верификации email: {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
)Поле модели
Добавьте верификацию к полям модели.
# 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'Интеграция с формами
Базовая форма
# 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('Пароли не совпадают.')
return cleaned_dataModel Form
# 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)
# Добавление валидатора к полю email
self.fields['email'].validators.append(validate_email_deliverable)Интеграция с представлениями
# 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():
# Дополнительная верификация с полным результатом
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
email = form.cleaned_data['email']
try:
result = client.verify(email)
# Создание пользователя с данными верификации
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, 'Регистрация успешна!')
return redirect('login')
except Exception as e:
messages.error(request, 'Регистрация не удалась. Пожалуйста, попробуйте снова.')
return render(request, self.template_name, {'form': form})Middleware
Верификация email во входящих запросах.
# 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 для верификации email-адресов в 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': 'Невалидный email-адрес'},
status=400
)
# Прикрепление результата верификации к запросу
request.email_verification = result
except Exception as e:
logger.warning(f'Ошибка верификации email: {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):
# Попытка извлечь из JSON body
if request.content_type == 'application/json':
try:
data = json.loads(request.body)
return data.get('email')
except json.JSONDecodeError:
pass
# Попытка извлечь из POST данных
return request.POST.get('email')Регистрация Middleware
# settings.py
MIDDLEWARE = [
# ... другие middleware
'myapp.middleware.EmailVerificationMiddleware',
]
EMAILVERIFY_PROTECTED_PATHS = [
'/api/register/',
'/api/subscribe/',
'/api/contact/',
]Кэширование
Кэшируйте результаты верификации для сокращения 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}'
# Использование
verification_service = EmailVerificationService()
if verification_service.is_valid(email):
# Обработка валидного email
passDjango REST Framework
Валидация в Serializer
# 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(
'Пожалуйста, введите корректный email-адрес.'
)
if result.result.disposable:
raise serializers.ValidationError(
'Одноразовые email-адреса не допускаются.'
)
# Сохранение результата для последующего использования
self.context['email_verification'] = result
except serializers.ValidationError:
raise
except Exception as e:
# Логируем, но не блокируем
pass
return valueПользовательское разрешение
# permissions.py
from rest_framework.permissions import BasePermission
from django.conf import settings
from emailverify import Client
class ValidEmailPermission(BasePermission):
"""Разрешает только запросы с валидными email-адресами."""
message = 'Требуется валидный email-адрес.'
def has_permission(self, request, view):
email = request.data.get('email')
if not email:
return True # Пусть другие валидаторы обрабатывают отсутствующий email
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(email)
return result.status != 'invalid'
except Exception:
return True # Разрешаем при ошибках APIКоманда управления
Массовая верификация email пользователей.
# 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 = 'Верификация всех email-адресов пользователей'
def add_arguments(self, parser):
parser.add_argument(
'--batch-size',
type=int,
default=100,
help='Количество email в пакете'
)
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'Верификация {total} email...')
# Сбор email для пакетной верификации
emails = list(users.values_list('email', flat=True)[:10000])
# Отправка пакетного задания
job = client.verify_bulk(emails)
self.stdout.write(f'ID задания: {job.job_id}')
# Ожидание завершения
while True:
status = client.get_bulk_job_status(job.job_id)
self.stdout.write(
f'Прогресс: {status.progress_percent}%',
ending='\r'
)
if status.status == 'completed':
break
time.sleep(5)
self.stdout.write('')
# Получение и обработка результатов
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('Верификация завершена!'))
# Вывод сводки
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}')Сигналы
Реагирование на события модели.
# 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):
"""Верификация email перед сохранением пользователя."""
# Пропуск, если email не изменился
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'Ошибка верификации email: {e}')Тестирование
# 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
# Не должно вызывать исключение
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')