EmailVerify LogoEmailVerify

Django

Email checker for Django. Python email verification in Django views and forms.

カスタムバリデータ、フォームフィールド、ミドルウェアを使用して、Django アプリケーションに EmailVerify を統合します。

インストール

pip install emailverify

設定

Django 設定に追加します:

# settings.py
EMAILVERIFY_API_KEY = os.environ.get('EMAILVERIFY_API_KEY')

# Optional settings
EMAILVERIFY_TIMEOUT = 10  # seconds
EMAILVERIFY_BLOCK_DISPOSABLE = True
EMAILVERIFY_BLOCK_ROLE_BASED = False
EMAILVERIFY_CACHE_DURATION = 3600  # 1 hour

カスタムバリデータ

再利用可能なメールバリデータを作成します。

# 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):
    """Validate that an email address is deliverable."""
    client = Client(api_key=settings.EMAILVERIFY_API_KEY)

    try:
        result = client.verify(email)

        if result.status == 'invalid':
            raise ValidationError(
                'Please enter a valid email address.',
                code='invalid_email'
            )

        if getattr(settings, 'EMAILVERIFY_BLOCK_DISPOSABLE', True):
            if result.result.disposable:
                raise ValidationError(
                    'Disposable email addresses are not allowed.',
                    code='disposable_email'
                )

        if getattr(settings, 'EMAILVERIFY_BLOCK_ROLE_BASED', False):
            if result.result.role:
                raise ValidationError(
                    'Role-based email addresses are not allowed.',
                    code='role_email'
                )

    except Exception as e:
        # Log error but don't block submission
        logger.warning(f'Email verification failed for {email}: {e}')


class EmailDeliverableValidator:
    """Class-based validator with customizable options."""

    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(
                    'Please enter a valid email address.',
                    code='invalid_email'
                )

            if self.block_disposable and result.result.disposable:
                raise ValidationError(
                    'Disposable email addresses are not allowed.',
                    code='disposable_email'
                )

            if self.block_role_based and result.result.role:
                raise ValidationError(
                    'Role-based email addresses are not allowed.',
                    code='role_email'
                )

        except ValidationError:
            raise
        except Exception as e:
            logger.warning(f'Email verification failed: {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('Passwords do not match.')

        return cleaned_data

モデルフォーム

# 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)
        # Add validator to email field
        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():
            # Additional verification with full result
            client = Client(api_key=settings.EMAILVERIFY_API_KEY)
            email = form.cleaned_data['email']

            try:
                result = client.verify(email)

                # Create user with verification data
                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, 'Registration successful!')
                return redirect('login')

            except Exception as e:
                messages.error(request, 'Registration failed. Please try again.')

        return render(request, self.template_name, {'form': form})

ミドルウェア

受信リクエストでメールを検証します。

# 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 to verify email addresses in POST requests."""

    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': 'Invalid email address'},
                            status=400
                        )

                    # Attach verification result to request
                    request.email_verification = result

                except Exception as e:
                    logger.warning(f'Email verification failed: {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):
        # Try JSON body
        if request.content_type == 'application/json':
            try:
                data = json.loads(request.body)
                return data.get('email')
            except json.JSONDecodeError:
                pass

        # Try POST data
        return request.POST.get('email')

ミドルウェアを登録

# settings.py
MIDDLEWARE = [
    # ... other 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}'


# Usage
verification_service = EmailVerificationService()

if verification_service.is_valid(email):
    # Process valid email
    pass

Django REST Framework

シリアライザーバリデーション

# 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(
                    'Please enter a valid email address.'
                )

            if result.result.disposable:
                raise serializers.ValidationError(
                    'Disposable emails are not allowed.'
                )

            # Store result for later use
            self.context['email_verification'] = result

        except serializers.ValidationError:
            raise
        except Exception as e:
            # Log but don't block
            pass

        return value

カスタムパーミッション

# permissions.py
from rest_framework.permissions import BasePermission
from django.conf import settings
from emailverify import Client

class ValidEmailPermission(BasePermission):
    """Only allow requests with valid email addresses."""

    message = 'A valid email address is required.'

    def has_permission(self, request, view):
        email = request.data.get('email')

        if not email:
            return True  # Let other validators handle missing email

        client = Client(api_key=settings.EMAILVERIFY_API_KEY)

        try:
            result = client.verify(email)
            return result.status != 'invalid'
        except Exception:
            return True  # Allow on API errors

管理コマンド

ユーザーメールの一括検証。

# 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 = 'Verify all user email addresses'

    def add_arguments(self, parser):
        parser.add_argument(
            '--batch-size',
            type=int,
            default=100,
            help='Number of emails per batch'
        )

    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'Verifying {total} emails...')

        # Collect emails for bulk verification
        emails = list(users.values_list('email', flat=True)[:10000])

        # Submit bulk job
        job = client.verify_bulk(emails)
        self.stdout.write(f'Job ID: {job.job_id}')

        # Wait for completion
        while True:
            status = client.get_bulk_job_status(job.job_id)
            self.stdout.write(
                f'Progress: {status.progress_percent}%',
                ending='\r'
            )

            if status.status == 'completed':
                break

            time.sleep(5)

        self.stdout.write('')

        # Get and process results
        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('Verification complete!'))

        # Print summary
        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):
    """Verify email before saving user."""
    # Skip if email hasn't changed
    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 verification failed: {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

        # Should not raise
        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')

関連リソース

On this page