Um eine E-Mail-Adresse in Django zu validieren, führen Sie drei Prüfungen aus, nicht eine – und das Framework liefert nur die erste mit. Djangos Validatoren bestätigen, dass eine Adresse korrekt geformt ist; sie fragen niemals, ob ihre Domain Mails empfangen kann oder ob das Postfach existiert. Echte Validierung ist geschichtet: erst die Syntax, dann ein DNS-Lookup, dann eine SMTP-Postfachprüfung. Diese Anleitung baut jede Schicht als Django-Validator auf, den Sie in ein Formular oder einen Serializer des Django REST Framework einsetzen können, und zeigt, wo eine Verifizierungs-API übernimmt. Sie knüpft dort an, wo die Python-Anleitung aufhört, sodass die dortige Argumentation zu DNS und SMTP unverändert übertragbar ist.
Die kurze Antwort#
Verwenden Sie validate_email (oder ein beliebiges EmailField) für die Syntax,
dnspython für das MX-Lookup und eine Verifizierungs-API für die
SMTP-Postfachprüfung – verdrahtet als drei Validatoren, den günstigsten zuerst, mit
Abbruch, sobald einer entscheidend ist. Öffnen Sie keine SMTP-Verbindungen aus Ihrem
Django-Prozess, um Postfächer zu prüfen: Der ausgehende Port 25 ist auf den meisten
Hosts blockiert, und die Antwort hängt von der Reputation der Sende-IP und von
Greylisting ab, das Sie nicht nachbauen wollen. Jede Schicht schließt Adressen
günstiger aus als die vorherige; nur die API kann eine Adresse einschließen.
Falls Ihnen die Pipeline neu ist, klärt
was E-Mail-Verifizierung ist zuerst die Begriffe.
Schicht 1: Formatvalidierung mit Djangos Validatoren#
Schicht eins beherrscht Django bereits. django.core.validators.validate_email ist
eine EmailValidator-Instanz, die bei einer fehlerhaften Adresse ValidationError
auslöst und bei einer gültigen None zurückgibt:
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
def is_valid_syntax(email: str) -> bool:
try:
validate_email(email)
return True
except ValidationError:
return False
Von Hand rufen Sie es selten auf. Jedes forms.EmailField, models.EmailField und
DRF-serializers.EmailField hängt EmailValidator automatisch an, sodass Ihnen
bereits die Deklaration eines Feldes die Syntaxvalidierung kostenlos liefert:
from django import forms
class SignupForm(forms.Form):
email = forms.EmailField() # EmailValidator runs during clean()
Machen Sie sich genau klar, was Ihnen das bringt. EmailValidator prüft die Form
anhand einer aus dem RFC abgeleiteten Grammatik – es löst niemals DNS auf, öffnet
niemals einen Socket und hat keine Ahnung, ob example.com existiert.
definitely-fake@gmail.com besteht. info@company-that-folded.com besteht.
typo@gmial.com besteht. Alle drei sind nicht zustellbar, und kein Validator, der
nur die Zeichenkette liest, wird Ihnen das jemals sagen – aus demselben Grund, aus
dem Regex-E-Mail-Validierung scheitert:
Syntax und Zustellbarkeit sind unterschiedliche Fragen. Die eine ist eine Tatsache
über die Zeichenkette; die andere eine Tatsache über das Internet.
Schicht 2: Nimmt die Domain Mails an?#
Dies ist die erste Schicht, die Django Ihnen nicht liefert, und sie lässt sich
günstig ergänzen. Eine Domain ohne MX-Records kann für niemanden Mails annehmen,
sodass ein einziges DNS-Lookup tote Domains, falsch geschriebene Firmennamen und
erfundene TLDs aussortiert. Django hat keinen eingebauten MX-Resolver, greifen Sie
also zu dnspython und verpacken Sie es in eine schlichte Funktion:
import dns.resolver # pip install dnspython
def has_mail_route(domain: str) -> bool:
try:
return len(dns.resolver.resolve(domain, "MX")) > 0
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.NoNameservers):
return False
Ein Django-Validator ist einfach eine aufrufbare Funktion, die bei ungültiger
Eingabe ValidationError auslöst; machen Sie die Prüfung also zu einer solchen, und
sie fügt sich direkt in die validators-Liste eines beliebigen Feldes ein:
from django.core.exceptions import ValidationError
def validate_deliverable_domain(email: str) -> None:
domain = email.rsplit("@", 1)[-1]
if not has_mail_route(domain):
raise ValidationError("This domain can't receive email.")
validate_deliverable_domain("jane@gmail.com") # passes
validate_deliverable_domain("jane@company-that-folded.com") # raises ValidationError
Wenn Sie Domains berücksichtigen möchten, die Mails über einen A-Record ohne MX
annehmen, weichen Sie bei leerem MX-Satz auf das Auflösen von A/AAAA aus – aber
eine MX-Prüfung deckt die überwiegende Mehrheit der echten Adressen ab.
Schicht 3: Existiert das Postfach tatsächlich?#
Schicht eins und zwei können eine Adresse nur ausschließen. Eine Domain kann
perfekte MX-Records veröffentlichen und trotzdem kein Postfach unter der Adresse
haben, die Ihnen vorliegt – noreply-9f2x@gmail.com hat gültige Syntax auf einer
aktiven Mail-Route und ist dennoch ein Postfach, das nie angelegt wurde. Ein
bestimmtes Postfach zu bestätigen bedeutet die SMTP-Zustellungskonversation: sich mit
dem Mail-Host verbinden, RCPT TO absetzen, die Antwort lesen und die Verbindung
trennen, bevor irgendetwas gesendet wird.
Im Prinzip könnten Sie das aus Django heraus mit smtplib skripten. In der Praxis
sollten Sie es nicht von Ihrem Anwendungsserver aus ausführen: Die meisten
Cloud-Hosts blockieren den ausgehenden Port 25, die Antwort hängt von der Reputation
der IP ab, von der aus Sie sich verbinden, und empfangende Server greylisten
unbekannte Absender und drosseln deren Rate – sodass eine Prüfung, die im lokalen
Test funktioniert, in der Produktion stillschweigend versagt oder Sie auf eine
Blockliste bringt. Wie E-Mail-Verifizierung funktioniert
geht die gesamte Pipeline durch, samt Catch-all-Domains. Dies ist die Schicht, die
es sich zu delegieren lohnt.
Qualisends Verify-Endpunkt führt die gesamte Pipeline aus – Syntax, DNS und die
SMTP-Postfachprüfung – von reputationsverwalteter Infrastruktur aus und liefert ein
Urteil. Rufen Sie ihn aus einem Validator heraus mit requests auf und lesen Sie
Ihren Schlüssel aus der Umgebung:
import os
import requests # pip install requests
QUALISEND_VERIFY_URL = "https://api.qualisend.com/v1/verify"
def verify(email: str) -> dict:
res = requests.post(
QUALISEND_VERIFY_URL,
headers={"Authorization": f"Bearer {os.environ['QUALISEND_API_KEY']}"},
json={"email": email},
timeout=10,
)
res.raise_for_status()
return res.json()["result"] # {"status", "score", "reason", "sub_flags"}
Die Antwort ist ein { "result": { ... } }-Umschlag. Lesen Sie result["status"] –
deliverable, risky, undeliverable oder unknown – zusammen mit einem score,
einem reason und sub_flags für Merkmale wie Rollen- oder Wegwerfadressen. Sehen
Sie in der API-Referenz die genauen Feldformen nach; hier benötigen
Sie nur den Status, um bei einer nicht zustellbaren Adresse eine Ausnahme auszulösen:
def validate_mailbox(email: str) -> None:
if verify(email)["status"] == "undeliverable":
raise ValidationError("We couldn't confirm a mailbox at this address.")
Sie könnten auch bei risky eine Ausnahme auslösen oder den score speichern und
die Adresse durchlassen – das ist eine Frage der Richtlinie. undeliverable
abzulehnen ist die sichere Standardeinstellung; die
Anleitung zur serverlosen Registrierung
behandelt, wie streng Sie am Erfassungspunkt sein sollten.
Die Schichten zusammenfügen, um eine E-Mail-Adresse in Django zu validieren#
Der idiomatische Ort, um die drei Schichten in einem Formular zusammenzusetzen, ist
eine clean_<field>-Methode. Django ruft clean_email erst auf, nachdem der
feldeigene EmailValidator bestanden hat, sodass die Syntax bereits erledigt ist;
Sie fügen die Domain- und Postfachprüfungen der Reihe nach hinzu, wobei jede früh
eine Ausnahme auslöst:
from django import forms
class SignupForm(forms.Form):
email = forms.EmailField() # layer 1 for free
def clean_email(self):
email = self.cleaned_data["email"] # syntax already passed
validate_deliverable_domain(email) # layer 2 — cheap, local
validate_mailbox(email) # layer 3 — the API round-trip
return email
Diese Reihenfolge ist der ganze Trick: Der EmailValidator des Feldes bricht bei
Müll ab, bevor Ihr Code läuft, die lokale DNS-Prüfung schließt tote Domains kostenlos
aus, und die API wird nur für Adressen aufgerufen, die beide bestanden haben.
Validierung in falscher Reihenfolge – oder das Überspringen der günstigen Schichten –
verbraucht bei jedem Tippfehler ein Guthaben.
Eine Sache sollten Sie vorab entscheiden: was passiert, wenn der API-Aufruf selbst
fehlschlägt. Ein Netzwerk-Timeout oder eine requests-Ausnahme innerhalb von
clean_email sollte einem echten Kunden keinen Validierungsfehler bescheren, den er
nicht beheben kann. Fangen Sie den Fehler der Anfrage getrennt von ValidationError
ab und behandeln Sie einen nicht erreichbaren Dienst als unknown statt als
undeliverable – lassen Sie die Registrierung durch und verifizieren Sie die Adresse
später erneut, anstatt die Registrierung an einem vorübergehenden Ausfall scheitern
zu lassen. Die Syntax- und MX-Schichten liefen bereits lokal, sodass Sie nur die
Schicht abmildern, die vom Netzwerk abhängt.
Da beide eigenen Validatoren Djangos ValidationError auslösen, lassen sich genau
dieselben zwei Funktionen unverändert in einen Serializer des Django REST Framework
einsetzen. Der validate_<field>-Hook von DRF fängt diese Ausnahme ab und verwandelt
sie in ein sauberes 400:
from rest_framework import serializers
class SignupSerializer(serializers.Serializer):
email = serializers.EmailField() # layer 1 for free
def validate_email(self, value):
validate_deliverable_domain(value) # layer 2
validate_mailbox(value) # layer 3
return value
Dies ist dieselbe Drei-Schichten-Form, die Sie in der Node.js- und der Python-Version dieser Anleitung finden – die Schichtung, nicht das Framework, ist es, was Validierung funktionieren lässt. Wenn Sie auswählen, welcher Verifizierungsdienst Schicht drei unterstützt, stellt der API-Vergleich die Optionen gegenüber.
Häufig gestellte Fragen#
Reicht Djangos EmailValidator aus, um eine E-Mail-Adresse zu validieren?#
Für die Syntax ja – validate_email (und jedes EmailField, das darauf zurückgreift)
ist die richtige Prüfung auf Schicht eins und besser als eine selbst gebaute Regex.
Aber es validiert die Form, nicht die Zustellbarkeit: Es löst niemals DNS auf und
kontaktiert keinen Mailserver, sodass ein Bestehen nur bedeutet "sieht aus wie eine
E-Mail", nicht "wird zugestellt". Kombinieren Sie es mit einem MX-Lookup und einer
SMTP-Postfachprüfung, bevor Sie der Adresse vertrauen.
Wie schreibe ich einen eigenen E-Mail-Validator in Django?#
Ein Validator ist eine beliebige aufrufbare Funktion, die den Wert entgegennimmt und
im Fehlerfall django.core.exceptions.ValidationError auslöst. Definieren Sie eine
Funktion wie validate_deliverable_domain(email), die bei einem Problem eine Ausnahme
auslöst und andernfalls None zurückgibt, und übergeben Sie sie dann in der
validators=[...]-Liste des Feldes oder rufen Sie sie aus der clean_email-Methode
eines Formulars auf. Dieselbe aufrufbare Funktion funktioniert im
validate_email-Hook eines DRF-Serializers, weil DRF Djangos ValidationError
ebenfalls abfängt.
Kann ich aus Django heraus ohne API prüfen, ob ein Postfach existiert?#
Teilweise. dnspython bestätigt, dass die Domain Mails annimmt, was tote Domains
kostenlos ausschließt und nichts weiter als eine kleine Abhängigkeit erfordert. Das
Postfach zu bestätigen bedeutet eine SMTP-Konversation, die Sie mit smtplib
versuchen können, aber nicht von Ihrem App-Server aus ausführen sollten – Port 25 ist
weitverbreitet blockiert und das Ergebnis hängt von der Reputation Ihrer IP ab. Genau
dafür ist ein Verifizierungsdienst da.
Sollte ich diese Prüfungen bei der Registrierung oder beim Bereinigen einer Liste ausführen?#
Beides, in unterschiedlicher Tiefe. Führen Sie die Syntax- und MX-Schichten synchron
in clean_email aus – sie sind schnell genug, um die Anfrage zu blockieren und
sofortiges Feedback zu geben – und reagieren Sie dort auch auf das Urteil der API.
Behalten Sie die aufwendigere Batch-Verifizierung für die Listenbereinigung und
Back-Office-Arbeit vor, wo Latenz keine Rolle spielt und Sie Adressen in großen
Mengen verarbeiten können.
Bereit, die SMTP-Schicht hinzuzufügen? Werfen Sie eine syntaktisch perfekte Adresse
in den kostenlosen E-Mail-Checker und beobachten Sie, wie eine
vom EmailField genehmigte Zeichenkette als undeliverable zurückkommt, und
verdrahten Sie dann dasselbe Urteil mit der API-Referenz in Ihre
Formulare – Copy-Paste-Beispiele inklusive.