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.
