Django Email Validation

Complete guide to email validation in Django

What You'll Learn

  • Install and configure VerifyForge in Django
  • Validate emails in Django forms
  • Create custom form validators
  • Validate emails in model save methods
  • Handle validation errors
  • Best practices for production

Prerequisites

  • Django 4+ installed
  • Python 3.8+ installed
  • Basic knowledge of Django
  • VerifyForge API key (get free key)

Installation

pip install verifyforge

Quick Start: Validate Single Email

from verifyforge import VerifyForge

client = VerifyForge(api_key="your_api_key")

# Validate an email
result = client.validate("user@example.com")

if result.data.is_valid:
    print(f"✓ Email is valid!")
    print(f"Reachability: {result.data.reachability}")
else:
    print(f"✗ Email is invalid")

Tutorial 1: Custom Form Validator

Step 1: Create Validator Function

# myapp/validators.py
from django.core.exceptions import ValidationError
from verifyforge import VerifyForge, VerifyForgeError
from django.conf import settings

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

def validate_email_verifyforge(email):
    """
    Custom validator that uses VerifyForge to validate email addresses.
    """
    try:
        result = client.validate(email)

        if not result.data.is_valid:
            raise ValidationError(
                'This email address is not valid.',
                code='invalid_email'
            )

        if result.data.disposable:
            raise ValidationError(
                'Temporary email addresses are not allowed.',
                code='disposable_email'
            )

        if result.data.reachability == 'invalid':
            raise ValidationError(
                'This email address cannot receive mail.',
                code='unreachable_email'
            )

    except VerifyForgeError as e:
        # Log error but don't block form submission
        print(f"Email validation error: {e}")

Step 2: Use in Django Form

# myapp/forms.py
from django import forms
from .validators import validate_email_verifyforge

class RegistrationForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField(
        validators=[validate_email_verifyforge]
    )
    password = forms.CharField(widget=forms.PasswordInput)

    def clean_email(self):
        email = self.cleaned_data.get('email')

        # Additional custom validation
        if email and email.endswith('@example.com'):
            raise forms.ValidationError('Example emails not allowed')

        return email

Step 3: Use in View

# myapp/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import RegistrationForm

def register(request):
    if request.method == 'POST':
        form = RegistrationForm(request.POST)

        if form.is_valid():
            # Save user
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']

            # Create user here...

            messages.success(request, 'Registration successful!')
            return redirect('login')
    else:
        form = RegistrationForm()

    return render(request, 'registration/register.html', {'form': form})

Tutorial 2: Model with Email Validation

# myapp/models.py
from django.db import models
from django.core.exceptions import ValidationError
from verifyforge import VerifyForge
from django.conf import settings

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

class Subscriber(models.Model):
    email = models.EmailField(unique=True)
    is_verified = models.BooleanField(default=False)
    reachability = models.CharField(max_length=20, blank=True)
    subscribed_at = models.DateTimeField(auto_now_add=True)

    def clean(self):
        """Validate email before saving"""
        super().clean()

        try:
            result = client.validate(self.email)

            if not result.data.is_valid:
                raise ValidationError({
                    'email': 'This email address is not valid.'
                })

            if result.data.disposable:
                raise ValidationError({
                    'email': 'Temporary email addresses are not allowed.'
                })

            # Store validation results
            self.is_verified = result.data.is_valid
            self.reachability = result.data.reachability

        except Exception as e:
            # Log error but allow save
            print(f"Validation error: {e}")

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)

    def __str__(self):
        return self.email

Tutorial 3: Django REST Framework Serializer

# myapp/serializers.py
from rest_framework import serializers
from verifyforge import VerifyForge
from django.conf import settings

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

class SubscriberSerializer(serializers.Serializer):
    email = serializers.EmailField()

    def validate_email(self, value):
        """Validate email using VerifyForge"""
        try:
            result = client.validate(value)

            if not result.data.is_valid:
                raise serializers.ValidationError(
                    'This email address is not valid.'
                )

            if result.data.disposable:
                raise serializers.ValidationError(
                    'Temporary email addresses are not allowed.'
                )

            if result.data.reachability == 'risky':
                raise serializers.ValidationError(
                    'This email appears risky. Please use another email.'
                )

            return value

        except Exception as e:
            raise serializers.ValidationError(
                'Email validation failed. Please try again.'
            )

class NewsletterSubscriptionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subscriber
        fields = ['email']

    def validate_email(self, value):
        """Custom validation with VerifyForge"""
        try:
            result = client.validate(value)

            if not result.data.is_valid:
                raise serializers.ValidationError('Invalid email address')

            return value

        except Exception as e:
            # Log but don't block
            print(f"Validation error: {e}")
            return value

Tutorial 4: API View for Email Validation

# myapp/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from verifyforge import (
    VerifyForge,
    AuthenticationError,
    InsufficientCreditsError,
    ValidationError as VFValidationError
)
from django.conf import settings

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

class ValidateEmailView(APIView):
    """API endpoint to validate email addresses"""

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

        if not email:
            return Response(
                {'error': 'Email is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            result = client.validate(email)

            return Response({
                'email': result.data.email,
                'is_valid': result.data.is_valid,
                'reachability': result.data.reachability,
                'disposable': result.data.disposable,
                'role_account': result.data.role_account,
                'free_provider': result.data.free_provider,
                'suggestion': result.data.suggestion,
            })

        except AuthenticationError:
            return Response(
                {'error': 'Invalid API key'},
                status=status.HTTP_401_UNAUTHORIZED
            )
        except InsufficientCreditsError:
            return Response(
                {'error': 'Insufficient credits'},
                status=status.HTTP_402_PAYMENT_REQUIRED
            )
        except VFValidationError as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )
        except Exception as e:
            return Response(
                {'error': 'Validation failed'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

class BulkValidateEmailView(APIView):
    """Bulk email validation endpoint"""

    def post(self, request):
        emails = request.data.get('emails', [])

        if not isinstance(emails, list) or len(emails) == 0:
            return Response(
                {'error': 'Emails array is required'},
                status=status.HTTP_400_BAD_REQUEST
            )

        if len(emails) > 100:
            return Response(
                {'error': 'Maximum 100 emails per request'},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            result = client.validate_bulk(emails)

            return Response({
                'results': [
                    {
                        'email': r.email,
                        'is_valid': r.is_valid,
                        'reachable': r.reachable
                    }
                    for r in result.results
                ],
                'summary': {
                    'total': result.summary.total,
                    'valid': result.summary.valid,
                    'invalid': result.summary.invalid,
                },
                'credits_used': result.credits_used,
            })

        except Exception as e:
            return Response(
                {'error': 'Bulk validation failed'},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

Tutorial 5: Django Management Command

# myapp/management/commands/validate_subscribers.py
from django.core.management.base import BaseCommand
from verifyforge import VerifyForge
from django.conf import settings
from myapp.models import Subscriber

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

class Command(BaseCommand):
    help = 'Validate all subscriber emails in bulk'

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

    def handle(self, *args, **options):
        batch_size = options['batch_size']

        # Get all unverified subscribers
        subscribers = Subscriber.objects.filter(is_verified=False)
        total = subscribers.count()

        self.stdout.write(f'Validating {total} subscribers...')

        # Process in batches
        for i in range(0, total, batch_size):
            batch = subscribers[i:i + batch_size]
            emails = [s.email for s in batch]

            try:
                result = client.validate_bulk(emails)

                # Update subscribers
                for subscriber, validation in zip(batch, result.results):
                    subscriber.is_verified = validation.is_valid
                    subscriber.reachability = validation.reachable
                    subscriber.save()

                self.stdout.write(
                    self.style.SUCCESS(
                        f'Processed batch {i // batch_size + 1}'
                    )
                )

            except Exception as e:
                self.stdout.write(
                    self.style.ERROR(f'Batch failed: {e}')
                )

        self.stdout.write(self.style.SUCCESS('Validation complete!'))

Run with:

python manage.py validate_subscribers --batch-size=100

Best Practices for Production

1. Environment Variables

# settings.py
import os

VERIFYFORGE_API_KEY = os.environ.get('VERIFYFORGE_API_KEY')
# .env
VERIFYFORGE_API_KEY=your_api_key_here

2. Caching Validation Results

# myapp/services.py
from django.core.cache import cache
from verifyforge import VerifyForge
from django.conf import settings

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

def validate_email_cached(email):
    """Validate email with caching"""
    cache_key = f'email_validation:{email}'

    # Check cache first
    cached_result = cache.get(cache_key)
    if cached_result:
        return cached_result

    # Validate and cache
    result = client.validate(email)
    cache.set(cache_key, result.data, timeout=3600)  # Cache for 1 hour

    return result.data

3. Async Validation with Celery

# myapp/tasks.py
from celery import shared_task
from verifyforge import VerifyForge
from django.conf import settings
from .models import Subscriber

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

@shared_task
def validate_email_async(email):
    """Asynchronously validate email"""
    try:
        result = client.validate(email)

        # Update subscriber if exists
        try:
            subscriber = Subscriber.objects.get(email=email)
            subscriber.is_verified = result.data.is_valid
            subscriber.reachability = result.data.reachability
            subscriber.save()
        except Subscriber.DoesNotExist:
            pass

        return {
            'email': email,
            'is_valid': result.data.is_valid,
        }

    except Exception as e:
        return {'email': email, 'error': str(e)}

Use in views:

from .tasks import validate_email_async

def subscribe(request):
    if request.method == 'POST':
        email = request.POST.get('email')

        # Validate asynchronously
        validate_email_async.delay(email)

        return JsonResponse({'message': 'Subscription pending validation'})

4. Error Handling Middleware

# myapp/middleware.py
import logging

logger = logging.getLogger(__name__)

class EmailValidationMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_exception(self, request, exception):
        from verifyforge import VerifyForgeError

        if isinstance(exception, VerifyForgeError):
            logger.error(f'VerifyForge error: {exception}')
            # Return graceful error response
            return JsonResponse(
                {'error': 'Email validation temporarily unavailable'},
                status=503
            )

Complete Example: Newsletter Subscription

# myapp/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.views.decorators.http import require_http_methods
from verifyforge import VerifyForge
from django.conf import settings
from .models import Subscriber

client = VerifyForge(api_key=settings.VERIFYFORGE_API_KEY)

@require_http_methods(["GET", "POST"])
def newsletter_subscribe(request):
    if request.method == 'POST':
        email = request.POST.get('email')

        if not email:
            messages.error(request, 'Email is required')
            return render(request, 'newsletter/subscribe.html')

        try:
            # Validate email
            result = client.validate(email)

            if not result.data.is_valid:
                messages.error(request, 'Please enter a valid email address')
                return render(request, 'newsletter/subscribe.html')

            if result.data.disposable:
                messages.error(
                    request,
                    'Temporary email addresses are not allowed'
                )
                return render(request, 'newsletter/subscribe.html')

            # Create or update subscriber
            subscriber, created = Subscriber.objects.get_or_create(
                email=email,
                defaults={
                    'is_verified': result.data.is_valid,
                    'reachability': result.data.reachability,
                }
            )

            if created:
                messages.success(request, 'Successfully subscribed!')
            else:
                messages.info(request, 'You are already subscribed')

            return redirect('newsletter_success')

        except Exception as e:
            messages.error(request, 'Subscription failed. Please try again.')
            return render(request, 'newsletter/subscribe.html')

    return render(request, 'newsletter/subscribe.html')
<!-- templates/newsletter/subscribe.html -->
{% load static %}

<form method="post">
  {% csrf_token %}

  <input
    type="email"
    name="email"
    placeholder="your@email.com"
    required
  />

  <button type="submit">Subscribe</button>

  {% if messages %}
    {% for message in messages %}
      <div class="alert alert-{{ message.tags }}">
        {{ message }}
      </div>
    {% endfor %}
  {% endif %}
</form>

Troubleshooting

Problem: API key not found

Solution: Ensure VERIFYFORGE_API_KEY is set in your environment or settings.

Problem: Validation slowing down requests

Solution: Use caching or async validation with Celery.

Problem: Too many API calls

Solution: Implement caching and validate only on form submission, not on every keystroke.

Next Steps