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')
# 선택적 설정
EMAILVERIFY_TIMEOUT = 10 # 초
EMAILVERIFY_BLOCK_DISPOSABLE = True
EMAILVERIFY_BLOCK_ROLE_BASED = False
EMAILVERIFY_CACHE_DURATION = 3600 # 1시간커스텀 검증기
재사용 가능한 이메일 검증기를 생성하세요.
# 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):
"""이메일 주소가 전송 가능한지 검증합니다."""
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(email)
if result.status == 'invalid':
raise ValidationError(
'유효한 이메일 주소를 입력해 주세요.',
code='invalid_email'
)
if getattr(settings, 'EMAILVERIFY_BLOCK_DISPOSABLE', True):
if result.result.disposable:
raise ValidationError(
'일회용 이메일 주소는 허용되지 않습니다.',
code='disposable_email'
)
if getattr(settings, 'EMAILVERIFY_BLOCK_ROLE_BASED', False):
if result.result.role:
raise ValidationError(
'역할 기반 이메일 주소는 허용되지 않습니다.',
code='role_email'
)
except Exception as e:
# 에러를 로깅하지만 제출은 차단하지 않음
logger.warning(f'{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(
'유효한 이메일 주소를 입력해 주세요.',
code='invalid_email'
)
if self.block_disposable and result.result.disposable:
raise ValidationError(
'일회용 이메일 주소는 허용되지 않습니다.',
code='disposable_email'
)
if self.block_role_based and result.result.role:
raise ValidationError(
'역할 기반 이메일 주소는 허용되지 않습니다.',
code='role_email'
)
except ValidationError:
raise
except Exception as e:
logger.warning(f'이메일 검증 실패: {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_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)
# 이메일 필드에 검증기 추가
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.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:
"""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': '유효하지 않은 이메일 주소입니다'},
status=400
)
# 검증 결과를 요청에 첨부
request.email_verification = result
except Exception as e:
logger.warning(f'이메일 검증 실패: {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 본문 시도
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')미들웨어 등록
# settings.py
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):
# 유효한 이메일 처리
passDjango 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(
'유효한 이메일 주소를 입력해 주세요.'
)
if result.result.disposable:
raise serializers.ValidationError(
'일회용 이메일은 허용되지 않습니다.'
)
# 나중에 사용하기 위해 결과 저장
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):
"""유효한 이메일 주소가 있는 요청만 허용."""
message = '유효한 이메일 주소가 필요합니다.'
def has_permission(self, request, view):
email = request.data.get('email')
if not email:
return True # 다른 검증기가 누락된 이메일 처리
client = Client(api_key=settings.EMAILVERIFY_API_KEY)
try:
result = client.verify(email)
return result.status != 'invalid'
except Exception:
return True # API 에러 시 허용관리 명령어
사용자 이메일 대량 검증.
# 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 = '모든 사용자 이메일 주소 검증'
def add_arguments(self, parser):
parser.add_argument(
'--batch-size',
type=int,
default=100,
help='배치당 이메일 수'
)
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}개 이메일 검증 중...')
# 대량 검증을 위한 이메일 수집
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):
"""사용자 저장 전 이메일 검증."""
# 이메일이 변경되지 않은 경우 건너뛰기
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'이메일 검증 실패: {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')