Django Web Sitesine Mesaj Formu Olan İletişim Sayfası Ekleme ve Otomatik Email Gönderme

İletişimin adeta ışık hızıyla geliştiği bu dönemlerde, o kadar hızlı olmasa da gelişen web sitelerinin vazgeçilmez iletişim araçlarının başında şüphesiz iletişim formları gelmektedir. Hemen hemen her web sitesinde yer alan İletişim sayfasındaki, ziyaretçilere site sahibi veya firmaya mesaj gönderilmesine olanak sağlayan bu mesaj formlarını oluşturmak Django'da oldukça basit.

Gerekli Olanlar:

1. Django 3.x veya 4.x yüklü ve çalışan bir web sitesi. (Django'da web sitesi nasıl oluşturulur kısmına girmiyorum zira konu onun ötesinde.)
2. Bir adet Gmail hesabı

İçindekiler:

1. Gmail Ayarlarını Hazırlama
2. Email Ayarlarını settings.py Dosyasına Ekleme
3. Url Alanına İletişim Sayfasını Ekleme
4. Model Oluşturma
5. Admin Paneline İletişim Formunu Ekleme
6. Form Oluşturma
7. Görünüm (view) Oluşturma
8. Template İçinde Formu Ekleme
9. EK BONUS BİLGİ (eklenti "attachment")

Django İletişim Formu Oluşturma ve Otomatik Email Gönderme

Not: Başlamadan önce belirtmeliyim ki Django'da form oluşturmak için birden çok yöntem mevcuttur. Bunlardan birincisi Form sınıfı (Form class) olup form.py dosyasında sınıfı forms.Form ile çağırma yöntemiyle kullanılır. Kısmen daha kompleks bir yapıya sahiptir. O yüzden biz daha yeni olan ModelForm yapısıyla dersimizi anlatacağız ve örneklerimiz de bu yönde olacak. ModelForm'un en büyük avantajı model yapısına oldukça benzemesi (hatta onu kullanması) ve form için normalde yapmamız gereken çoğu şeyi bizim için kendiliğinden yapabiliyor olmasıdır. Hadi işimize koyulalım.

1. Gmail Ayarlarını Hazırlama: İlk olarak email gönderimini (SMTP) Gmail üzerinden kullanabilmek için ayarlarımızda bazı değişiklikler yapmamız gerekiyor. Şimdi gmail hesabınızdan sırasıyla;

- Ayarlar > Güvenlik sayfasına gelin ve biraz aşağıdaki Google'da oturum açma bölümünde 2 Adımlı Doğrulamayı aktif edin.

- Sonrası yine aynı yerden Uygulama şifreleri bölümüne girin ve "Uygulama şifresi oluşturmak istediğiniz uygulamayı ve cihazı seçin." yazısının altındaki Uygulama seçin alanına tıklayarak Diğer seçeneğini seçin. 

Gmail ayarları 1

- Uygulama adı olarak istediğiniz şeyi yazabilirsiniz "ben Django yazdım" ve ardından OLUŞTUR butonuna basın.

Gmail ayarları 2

- Açılan penceredeki üretilen 16 haneli şifreyi bir yere kaydedin. Bu şifreyi Django ayarlarında kullanacağız.

2. Email Ayarlarını settings.py Dosyasına Ekleme: İkinci aşamada ise Django projemizin settings.py dosyasına aşağıdaki ilgili ayarları eklemeliyiz.

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.gmail.com"
# EMAIL_PORT = 587  # üretim modunda bu seçeneği kullanın
EMAIL_PORT = 465 # bu seçeneği debug modu aktifken kullanın
# EMAIL_USE_TLS = True  # üretim modunda bu seçeneği kullanın
EMAIL_USE_TLS = False # bu seçeneği debug modu aktifken kullanın
# EMAIL_USE_SSL = False  # üretim modunda bu seçeneği kullanın
EMAIL_USE_SSL = True # bu seçeneği debug modu aktifken kullanın
EMAIL_HOST_USER = emailadresiniz@gmail.com
EMAIL_HOST_PASSWORD = gmail_uygulama_şifrenizi_buraya_yazın
EMAIL_ADMIN = emailadresiniz@gmail.com
DEFAULT_FROM_EMAIL = emailadresiniz@gmail.com

3. Url Alanına İletişim Sayfasını Ekleme: İletişim sayfamızın tarayıcıda açılabilmesi için url ayarlarını oluşturuyoruz.

>>> ./uygulamadizini/urls.py

urlpatterns = [
    ...
    path('iletisim/', ContactFormView.as_view(), name='iletisim'),
    ...
]

4. Model Oluşturma: Gönderilen mesajların veri tabanında saklanabileceği modelimizi aşağıdaki gibi oluşturuyoruz.

>>> ./uygulamadizini/models.py

class ContactForm(models.Model):
    name = models.CharField("İsim Soyisim", max_length=150)
    email = models.EmailField("Email Adresi", max_length=150)
    subject = models.CharField("Konu", max_length=300)
    message = models.TextField("Mesaj", max_length=2000)
    created_at = models.DateTimeField("Gönderilme Tarihi", auto_now=True)

    def __str__(self):
        return self.email

    class Meta:
        verbose_name_plural = "İletişim Mesajları"
        verbose_name = "İletişim Mesajı"

Yukarıda İsim, Email, Konu ve Mesaj alanlarının ziyaretçi tarafından doldurulabilmesi için veri tabanı tablosu modeli oluşturduk ve gönderilen mesajın gönderilme tarihini de DateTimeField ile auto_now=True ekleyerek otomatik olarak oluşturulmasını sağladık.

Not: Modelimizi oluşturduktan sonra python manage.py makemigrations ve ardından python manage.py migrate komutlarını kullanarak veritabanında tabloları oluşturmayı unutmuyoruz.

5. Admin Paneline İletişim Formunu Ekleme: Oluşturduğumuz modelin admin panelimizde gözükmesi için dilerseniz uygulama dizinindeki admin.py dosyasına sadece admin.site.register(ContactForm) ekleyebilir veya daha güzel ve detaylı bir görünüm için aşağıdaki kodu ekleyebilirsiniz.

>>> ./uygulamadizini/admin.py

@admin.register(ContactForm)
class ContactFormAdmin(admin.ModelAdmin):
    list_display = ["name", "email", "subject", "created_at"]
    readonly_fields = ["name", "email", "subject", "message", "created_at"]
    fieldsets = (
        ("MESAJLAR", {
            "fields": ("name", "email", "subject", "message", "created_at")
        }),
    )

    def has_delete_permission(self, request, obj=None):
        return True

    def has_add_permission(self, request):
        return False

Yukarıdaki kodda readonly_fields değişkeni sayesinde tüm alanlar sadece okunabilir hale getirildi ve has_delete_permission için kullandığımız True ile silme yetkisi verilirken has_add_permission için tanımladığımız False bize manuel olarak mesaj ekleyememe kısıtı getirdi. Admin paneli şöyle gözükecektir:

İletişim admin paneli

6. Form Oluşturma: İkinci aşama olarak kullanıcılarla etkileşime geçecek olan form modelini oluşturuyoruz.

>>> ./uygulamadizini/forms.py  
'''Dilerseniz forms.py dosyasını bu şekilde değil, bir klasör altında daha düzenli olarak da oluşturabilirsiniz. Örneğin: < ./uygulamadizini/forms/contactform.py > Tabi bunun için o dizinde __init__.py dosyası oluşturup içerisine < from .contactform import * > kodunu ekleyerek yerini belirtmelisiniz.'''

class ContactmeForm(ModelForm):
    class Meta:
        model = ContactForm
        fields = ["name", "email", "subject", "message"]  # veya bunun yerine '__all__' ekleyerek tüm alanları çağırabilirsiniz.

Gördüğünüz gibi ModelForm'u oluştururken ContactForm modelini referans olarak çağırıyoruz ve formda yer alan doldurulması gereken alanları belirtiyoruz. Bu kadar. Siz Django'da bir form modeli oluşturmanın ne kadar kolay ve harika olduğunu düşünüyorken birkaç ekstra bilgi vereyim. Formlar aslında oldukça detaylı bir yapıdır ve doğrudan form modeli içerisinde html arayüzünü etkileyecek stillerin değiştirilmesine olanak verebilir. Örneğin:

class ContactmeForm(ModelForm):
    name = MyFormField(
        max_length=50,
        required=False,
        label="İsim"
        help_text='İsim alanı en fazla 50 karakter olmalıdır ve doldurmak zorunlu değildir.',
    )

    class Meta:
        model = ContactForm
        fields = ["name", "email", "subject", "message"]
        widgets = {
            'message': Textarea(attrs={'cols': 80, 'rows': 20}),  # mesaj alanı için özel boyut belirledik.
        }
        field_classes = {
            'subject': MyFieldClass,  # konu alanı için özel class belirledik. Bu class için artık style.css dosyasında dilediğiniz stili ekleyebilirsiniz.
        }

Gördüğünüz gibi ModelForm'lar stilllerle oynamadan doğrudan modelin formunun içerisinden ayarlamalar yapmaya imkan eriyor. Detaylı bilgi için Django'nun dokümanlarından ModelForm bölümünü inceleyebilirsiniz. Şimdi devam edelim.

7. Görünüm (view) Oluşturma: Django'nun formlar için bize sunduğu nimetler arasında bir de FormView bulunmaktadır. Standart görünüm yapılarına göre FormView de birçok işimizi bizim için yapar.

>>> ./uygulamadizini/views.py  

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


class ContactFormView(FormView):
    template_name = 'uygulamadizini/iletisim.html'
    form_class = ContactmeForm
    success_url = '/iletisim'

    def form_valid(self, form):
        name = form.cleaned_data['name']
        subject = form.cleaned_data['subject']
        message = form.cleaned_data['message']
        email = form.cleaned_data['email']

        mail = EmailMessage(
            f'{name} Tarafından Mesaj Gönderildi',
            f'Konu: {subject}\n\nEmail: {email}\n\nMesaj: {message}\n\n',
            f'"YENİ MESAJ" <{email}>', # email'in kimden (from) geldiğini yazdık.
            [settings.EMAIL_ADMIN], # email'in kime (to) gideceğini belirledik.
            reply_to=[f'{email}'], # gmail'de yanıtlama yapıldığında otomatik olarak mesaj gönderenin email adresini seçtirdik.
        )
        mail.send()
        form.save()
        messages.success(self.request, 'Mesajınız başarıyla gönderildi.')
        return super().form_valid(form)

Yukarıdaki kodda gördüğünüz üzere FormView doğrulama sayfasına yönlendirmeden tutun da formun doğrulanmasına kadar bir çok şeyi kolayca yapabilmemize olanak sağlıyor. Email gönderimi için django'nun bize sunduğu send_mail() fonksiyonunu da kullanabilirdik, lakin esnekliği bakımından benim daha çok hoşuma giden EmailMessage() sınıfını kullandık.

Gördüğünüz gibi içinde Python'un f-string yapısından faydalandık. Dilersek bir şablon kullanabilir, düz metin veya html kodları da kullanabilirdik. f-String bize metin içerisinde yukarıda tanımlı değişkenlerimizi metin yapısını bozmadan kolayca kullanabilme imkanı sağlıyor. 

Mesaj gönderildiğinde ise template üzerinde belirecek doğrulama mesajını "mesajların kullanımının birden çok yöntemi bulunmakla birlikte" buradan belirleyebiliyoruz. Mesajların template üzerinde gözükmesi için sadece şu kodları eklemeniz yeterli:

{% if messages %}
<div class="alert alert-success text-center" role="alert">
    {% for message in messages %}
        {% if message.tags %}
            <span>{{ message }}</span>
        {% endif %}
    {% endfor %}
</div>
{% endif %}

8. Template İçinde Formu Ekleme: Sıra son aşama olan html sayfasında formu görüntülemede.

>>> ./uygulamadizini/templates/uygulamadizini/iletisim.html 
<!--Bu ideal template dizin yerleşimi. Ancak siz ana projedeki template klasörünü de kullanabilirsiniz.-->

<form method="post" class="needs-validation" novalidate>
    {% csrf_token %}
    {{ form|crispy }}
    <button type="submit">Gönder</button>
</form>

Django'da template içerisinde form eklemek bu kadar kolay. form tag'i tüm bilgileri bizim için back-end içinden alıp derliyor. Ancak form'un yanındaki crispy tag'ine dikkat etmiş olmalısınız. Onun görevi sadece formun güzel gözükmesini sağlamak. İsterseniz kullanmayabilirsiniz. Ama eğer kullanmak istiyorsanız aşağıdaki düzenlemeleri yapmanız gerekecek.

- Öncelikle django-crispy-forms eklentisini yükleyin. (pip install django-crispy-forms) Detaylı bilgi için Crispy Forms Kullanım Kılavuzunu inceleyebilirsiniz.

- Ardından template dosyasının en üstüne {% load crispy_forms_filters %} etiketini ekleyin ve form etiketinin yanına |crispy tag'ini ekleyin.

- Django settings.py dosyasının içindeki INSTALLED_APPS bölümünde sona 'crispy_forms' uygulamasını ekleyin.

- Yine settings.py dosyasında aşağıda crispy formun ayarlarını ekleyin.

CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap4"
CRISPY_TEMPLATE_PACK = "bootstrap4"

İşte bu kadar. Artık tam iletişim formumuz tam olarak çalışır hale gelmiş durumda.

EK BONUS BİLGİ: Eğer email formunda eklenti (attachment) kullanmak isteseydik şu şekilde düzenleme yapmamız gerekiyordu. Ayrıca belirtmek isterim ki bu yöntemle gmailde yüklenilen eklenti indirilebilir ve görülebilir harici eklenti olarak gözükmektedir.

- Öncelikle modelimize eklenti için alan eklemeliyiz.

>>> ./uygulamadizini/models.py

class ContactForm(models.Model):
    ...
    cv_file = models.FileField(upload_to='static/cv_files/', help_text='Desteklenen Dosya Formatı: pdf, doc, docx', validators=[FileExtensionValidator(allowed_extensions=['pdf', 'doc', 'docx'])])
    ...

- Ardından görünümde de aynı değişikliği yapmalıyız.

>>> ./uygulamadizini/views.py  

def form_valid(self, form):
    ...
    cv_file = form.cleaned_data['cv_file']
    ...

    mail = EmailMessage(
        ...
    )
    mail.attach_file(os.path.join(BASE_DIR / 'static/cv_files/', cv_file.name)),
    mail.send()
    ...
    return super().form_valid(form)

- Ve son olarak template form alanında da enctype="multipart/form-data" eklememiz gerekiyor.

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

Eğer teknik bir sorunuz varsa veya yardıma ihtiyacınız olursa aşağıda yorumlara yazmanız yeterli olacaktır. Ayrıca yazımız beğendiyseniz paylaşarak bana destek olabilirsiniz :)

Paylaş

Yeni Blog Yazılarımdan Haberdar Olun

Yeni yazılarımdan anında haberdar olmak için email listeme abone olun. Size spam göndermeyeceğime söz veriyorum!