biotech Django & Python

Django'da İletişim Formu ve Email Entegrasyonu: Gmail, Dosya Eki, reCAPTCHA v3

AK
Ali Kasımoğlu
06 Haz 2022 schedule 4 dk okuma
Django'da {p}İletişim Formu{/p} ve Email Entegrasyonu: Gmail, Dosya Eki, reCAPTCHA v3
analytics

Insight Density

groups Hedef Kitle: Deneyimli
65 Score

Teknik karmaşıklık ve içerik yoğunluğuna göre hesaplandı.

Son güncelleme: Nisan 2026 · AnomixLabs Teknik Ekibi

İletişim formu bir sitenin müşteriye açılan kapısıdır. Spam'e açık, hatalı veya yavaş bir form doğrudan gelir kaybıdır. Doğru yapılmış bir form ise güven verir.

1. ContactForm Modeli ve ModelForm

from django.db import models

class ContactMessage(models.Model):
    name = models.CharField('Ad Soyad', max_length=100)
    email = models.EmailField('E-Posta')
    subject = models.CharField('Konu', max_length=200)
    message = models.TextField('Mesaj')
    attachment = models.FileField(
        'Dosya Eki', upload_to='contact/', blank=True, null=True
    )
    ip_address = models.GenericIPAddressField(blank=True, null=True)
    is_read = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'İletişim Mesajı'
        ordering = ['-created_at']
from django import forms
from .models import ContactMessage

ALLOWED_EXTENSIONS = ['pdf', 'doc', 'docx', 'jpg', 'png', 'zip']
MAX_FILE_SIZE = 5 * 1024 * 1024  # 5 MB

class ContactForm(forms.ModelForm):
    honeypot = forms.CharField(required=False, widget=forms.HiddenInput)

    class Meta:
        model = ContactMessage
        fields = ['name', 'email', 'subject', 'message', 'attachment']

    def clean_honeypot(self):
        if self.cleaned_data.get('honeypot'):
            raise forms.ValidationError('Bot tespiti')
        return ''

    def clean_attachment(self):
        f = self.cleaned_data.get('attachment')
        if not f:
            return f
        ext = f.name.rsplit('.', 1)[-1].lower()
        if ext not in ALLOWED_EXTENSIONS:
            raise forms.ValidationError(f'İzin verilmeyen format. Kabul edilenler: {ALLOWED_EXTENSIONS}')
        if f.size > MAX_FILE_SIZE:
            raise forms.ValidationError('Dosya 5 MB sınırını aşıyor.')
        return f

2. FormView ile Güvenli POST İşlemi

from django.views.generic.edit import FormView
from django.core.mail import EmailMessage
from django.contrib import messages
from django.conf import settings
from .forms import ContactForm

class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm
    success_url = '/iletisim/tesekkurler/'

    def form_valid(self, form):
        msg = form.save(commit=False)
        msg.ip_address = self.request.META.get('REMOTE_ADDR')
        msg.save()
        self._send_email(msg)
        messages.success(self.request, 'Mesajınız başarıyla gönderildi!')
        return super().form_valid(form)

    def _send_email(self, msg):
        email = EmailMessage(
            subject=f'[İletişim] {msg.subject}',
            body=(
                f'Gönderen: {msg.name} \n'
                f'Mesaj:\n{msg.message}'
            ),
            from_email=settings.DEFAULT_FROM_EMAIL,
            to=[settings.CONTACT_EMAIL],
            reply_to=[msg.email],
        )
        if msg.attachment:
            email.attach_file(msg.attachment.path)
        email.send(fail_silently=False)

3. HTML Şablonu ve Form Render

İletişim formunun template'de görünmesi için iki yaklaşım var. Önerileni: django-crispy-forms ile otomatik stillendirilmiş çıktı:

Django admin panelinde iletişim formu mesajları listesi

$ pip install crispy-tailwind  # Tailwind için
# veya: pip install crispy-bootstrap5
INSTALLED_APPS += ['crispy_forms', 'crispy_tailwind']
CRISPY_ALLOWED_TEMPLATE_PACKS = 'tailwind'
CRISPY_TEMPLATE_PACK = 'tailwind'
{% load crispy_forms_tags %}

<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form|crispy }}
  <button type="submit">Gönder</button>
</form>

{# Başarı/hata mesajları #}
{% for message in messages %}
  <div class="alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}

Crispy-forms kullanmak istemiyorsanız Django'nun yerleşik render yöntemi:

<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Gönder</button>
</form>

Önemli: Dosya eki alanı içeren formlarda enctype="multipart/form-data" zorunludur — aksi takdirde yüklenen dosya POST gövdesinde yer almaz ve request.FILES boş gelir.

4. Gmail SMTP Yapılandırması

Gmail hesabınızda 2FA etkin olmalıdır. 'Uygulama Şifresi' oluşturun (myaccount.google.com → Güvenlik → Uygulama Şifreleri). Normal hesap şifrenizi kullanmayın:

Gmail uygulama şifresi oluşturma — adım 1: uygulama seçimi Gmail uygulama şifresi oluşturma — adım 2: şifre oluştur

EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=sirket@gmail.com
EMAIL_HOST_PASSWORD=xxxx-xxxx-xxxx-xxxx  # Uygulama şifresi
DEFAULT_FROM_EMAIL=AnomixLabs 
CONTACT_EMAIL=info@anomixlabs.com
EMAIL_HOST = env('EMAIL_HOST')
EMAIL_PORT = env.int('EMAIL_PORT', 587)
EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', True)
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL')
CONTACT_EMAIL = env('CONTACT_EMAIL')

5. reCAPTCHA v3 Entegrasyonu

reCAPTCHA v3 görünmez çalışır — kullanıcıdan 'ben robot değilim' tıklaması istemez. Google 0.0-1.0 arası bir güven skoru döndürür, 0.5 üzeri normal kullanıcı kabul edilir:

$ pip install django-recaptcha
INSTALLED_APPS += ['django_recaptcha']

RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY')
RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PRIVATE_KEY')
# v3 için minimum skor (0.0 - 1.0)
RECAPTCHA_REQUIRED_SCORE = 0.5
from django_recaptcha.fields import ReCaptchaField
from django_recaptcha.widgets import ReCaptchaV3

class ContactForm(forms.ModelForm):
    captcha = ReCaptchaField(widget=ReCaptchaV3)
    # ...

6. Honeypot Spam Koruması

CSS ile gizlenmiş bir alan — botlar tüm alanları doldurur, gerçek kullanıcılar görmez. reCAPTCHA API'sine ihtiyaç duymadan ek koruma katmanı sağlar:

class ContactForm(forms.ModelForm):
    # Botların göreceği ama kullanıcıların görmeyeceği alan
    website = forms.CharField(required=False, widget=forms.HiddenInput)

    def clean_website(self):
        if self.cleaned_data.get('website'):
            # Bot bu alanı doldurdu — form geçersiz
            raise forms.ValidationError('Spam tespit edildi')
        return ''

7. Rate Limiting ile Kötüye Kullanım Önleme

from django.core.cache import cache

class ContactView(FormView):
    def form_valid(self, form):
        ip = self.request.META.get('REMOTE_ADDR', '')
        cache_key = f'contact_rate_{ip}'
        count = cache.get(cache_key, 0)

        if count >= 3:  # Saatte 3 mesaj limiti
            messages.error(self.request, 'Çok fazla mesaj gönderdiniz. Lütfen bekleyin.')
            return self.form_invalid(form)

        cache.set(cache_key, count + 1, timeout=3600)  # 1 saat
        return super().form_valid(form)

8. Async Email: Celery ile (Opsiyonel)

Email gönderimi ağ isteği gerektirdiğinden yavaş olabilir. Büyük hacimli projeler için Celery ile arka planda gönderin:

from celery import shared_task
from django.core.mail import EmailMessage

@shared_task
def send_contact_email_task(subject, body, from_email, to_emails, reply_to):
    email = EmailMessage(
        subject=subject,
        body=body,
        from_email=from_email,
        to=to_emails,
        reply_to=[reply_to],
    )
    email.send()

# views.py'de çağırma
send_contact_email_task.delay(
    subject=f'[İletişim] {msg.subject}',
    body=msg.message,
    from_email=settings.DEFAULT_FROM_EMAIL,
    to_emails=[settings.CONTACT_EMAIL],
    reply_to=msg.email,
)

9. SPF ve DKIM: Email Deliverability

Gönderdiğiniz emailler spam klasörüne düşüyorsa bu teknik DNS kayıtları eksik demektir:

  • SPF: DNS'e TXT kaydı ekleyin: v=spf1 include:_spf.google.com ~all — Gmail'in sizin adınıza email gönderebileceğini belirtir.
  • DKIM: Google Workspace veya SendGrid panelinden CNAME kayıtları alın ve DNS'e ekleyin. Emailin imzalı gönderildiğini kanıtlar.
  • DMARC: v=DMARC1; p=quarantine; rua=mailto:dmarc@domain.com — SPF/DKIM başarısız olursa ne yapılacağını belirtir.

10. Test Ortamı: Mailtrap veya django-mail-panel

# Geliştirme: emailler konsola yazdırılır, gönderilmez
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

# Veya Mailtrap (gerçek SMTP gibi davranır, ama gerçek email gönderilmez)
EMAIL_HOST = 'sandbox.smtp.mailtrap.io'
EMAIL_HOST_USER = env('MAILTRAP_USER')
EMAIL_HOST_PASSWORD = env('MAILTRAP_PASS')
EMAIL_PORT = 2525

Özet

Profesyonel Django iletişim formu: ModelForm (validasyon), FormView (GET/POST), EmailMessage (dosya ekiyle), Gmail App Password (SMTP), reCAPTCHA v3 + honeypot (spam koruma), rate limiting (kötüye kullanım önleme), SPF/DKIM (deliverability). Production'da test etmek için Mailtrap kullanın.

Sıkça Sorulan Sorular

Gmail limitleri production'da sorun çıkarır mı? expand_more
Gmail ücretsiz hesaplar için günde 500 email limiti var. Kurumsal hacim için: 1) Google Workspace ($6/ay/kullanıcı, günde 2.000 limit), 2) SendGrid (10.000/ay ücretsiz, sonrası ücretli), 3) AWS SES (1.000/gün ücretsiz EC2'den). Production'da Gmail yerine bu alternatifler tercih edilir. Django'da backend değişikliği settings.py'de EMAIL_HOST güncellemesiyle yapılır.
Dosya eki boyut sınırı nasıl ayarlanır? expand_more
İki katmanda kontrol: 1) forms.py clean_attachment() metodunda Python seviyesinde boyut kontrolü, 2) Nginx/settings.py'de HTTP request body limiti. Nginx için: client_max_body_size 10m; (10 MB). Django için: DATA_UPLOAD_MAX_MEMORY_SIZE = 10485760 (10 MB). Her iki limit de uyumlu olmalı. Dosya ekleri MEDIA_ROOT'a kaydedilir — düzenli temizlik cron'u ekleyin.
Async email için Celery yerine ne kullanabilirim? expand_more
Celery güçlü ama ağır. Alternatifler: 1) django-Q2: Celery'ye benzer ama kurulumu daha kolay. 2) Dramatiq: Celery alternatifi, daha az karmaşık. 3) Django'nun built-in async view'ları (ASGI) ile async email: async def form_valid() içinde await send_mail_async(). 4) Küçük projeler için senkron email kabul edilebilir — kullanıcı 100-300ms bekler.
reCAPTCHA v2 mi v3 mü tercih edilmeli? expand_more
v3 tercih edilir çünkü kullanıcıya hiçbir challenge göstermez — arka planda skor hesaplar. v2 'ben robot değilim' kutucuğu mobilde rahatsız edici. Ancak v3 skoru düşük meşru kullanıcıları bloke edebilir — skoru çok yüksek tutmayın (0.5 yeterli). reCAPTCHA'ya ek olarak honeypot kullanmak iki katmanlı koruma sağlar.
Email gönderimi başarısız olursa ne yapmalı? expand_more
fail_silently=False ile hata fırlatılır. Production'da bunu bir try/except bloğu ile yakalayın, hatayı logları veya Sentry'ye gönderin, kullanıcıya 'mesajınız kaydedildi, en kısa sürede döneceğiz' deyin. Mesaj veritabanına kaydedildiğinden email başarısız olsa da manuel olarak takip edilebilir.
İletişim formunu admin panelinde nasıl yönetirim? expand_more
ContactMessage modelini admin.py'e kaydedin: list_display=['name', 'email', 'subject', 'is_read', 'created_at'], list_filter=['is_read'], search_fields=['name', 'email', 'subject']. is_read alanına tıklayarak mesajları okundu işaretleyin. Admin'deki 'Yanıtla' butonuyla otomatik email template açmak için custom admin action yazabilirsiniz.
GDPR uyumu için formda ne yapmalıyım? expand_more
AB'deki kullanıcılar için: 1) Gizlilik politikası onay checkbox'ı ekleyin (is_required=True), 2) Toplanan verilerin ne kadar süre saklandığını belirtin (ör: 2 yıl), 3) Mesajları belirli süreden sonra otomatik silmek için celery beat ile cron görevi kurun, 4) Kullanıcının verisini silme hakkı için bir 'verimi sil' sayfası ekleyin. Django GDPR paketi (django-gdpr-assist) bu süreçleri kolaylaştırır.
Etiketler: #Django #Email #SMTP #Gmail #ModelForm #FormView #reCAPTCHA #Dosya Eki #SPF #DKIM #Honeypot
share

Bu Makaleyi Paylaş

Bilgiyi ağınızla paylaşarak bize destek olun.

AK

Ali Kasımoğlu

Full-stack Geliştirici & AnomixLabs Kurucusu

Python ve Django ekosisteminde uzmanlaşmış bir yazılım geliştirici. Modern web mimarileri, yapay zeka entegrasyonları ve minimalist kullanıcı deneyimleri üzerine odaklanıyor. AnomixLabs çatısı altında, karmaşık problemleri yalın ve etkili dijital çözümlere dönüştürmeyi hedefliyor.

psychology
psychology

Makale Hakkında Soru Sorun

AnomixAI · Makale içeriğine dayalı yanıtlar

5 soru hakkı
Yalnızca makale içeriği hakkında 0/500
forward_to_inbox

Geleceği Çözümleyin.

Enterprise yapay zeka, yazılım mimarisi ve dijital dönüşüm üzerine aylık brifingi alan 5.000+ mühendis ve kurucuya katılın. Spam yok.