26 Temmuz 2014 Cumartesi

Yönetim Paneli Özelleştirme

Bu yazı da kısaca yönetim panelini nasıl özelleştirebiliriz ona değineceğiz. Django yönetim panelinde ana şablon olarak admin/base.html'i kullanır. Ve bize de kendi yönetim şablonumuzu oluşturma imkanı verir. Bu yönetim şablonunu sablonlar/admin/base_site.html olarak arar. Yönetim panelinde varsayılan olarak header kısmında Django Yönetimi yazmaktadır. Şimdi basit bir şekilde bunu nasıl değiştirebileceğimizi görelim.
İlk önce şun söyleyelim oluşturacağımız base_site.html artık bizim yönetim şablonumuz olacak. Bunun içine sadece 'merhaba' yazarsanız yönetim panelinde de sadece 'merhaba' yazısını görürsünüz. Django'nun kendi yönetim panelinin devam edebilmesi için bizim kendi şablonundan kalıtım almamız gerekiyor.
{% extends "admin/base.html" %}
{% load i18n %}

{% block title %}{{ title }} | {% trans 'Başlık' %}{% endblock %}

{% block branding %}

{% trans 'Header Başlık' %}

{% endblock %} {% block nav-global %}{% endblock %}
Burada ilk önce extends ifadesi ile kalıtım aldık. Kalıtım alırken her şey olduğu gibi alınır. Ancak aynı block ifadeleri hem kalıtım alınan şablonda hem de kalıtımı alan şablonda var ise kalıtımı alan şablondaki gösterilir. Biz block branding ifadesini yeniden yazdığımız için bizim yazdığımı görünecektir.
Bu şekilde block ifadelerini kullanarak yönetim panelini özelleştirebilirsiniz. Django'nun yönetim şablonlarını buradan inceleyebilirsiniz.

24 Temmuz 2014 Perşembe

ModelAdmin Metodları

ModelAdmin içerisinde kullanılabilen metodlar bulunmaktadır. Bu yazı da bazı metodların ne işe yaradıklarını ve nasıl kullanıldıklarını anlatmaya çalışacağız.
save_model(self, request, obj, form, change)
Bir model elemanı kaydedilirken olmasını istediğimiz şeyleri bu metot içerisinde yapabiliriz. Örneğin elimizde şöyle modeller olsun;
class Yazilar(models.Model):
    yazar = models.ForeignKey(User,blank=True,null=True)
    baslik = models.CharField(max_length=100)
    icerik = models.TextField()
Burada yazı kaydedilirken yazar kısmı giriş yapmış kullanıcı olsun istiyoruz. İşte bunun için save_model() metodunu kullanabiliriz.
class YazilarAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.yazar = request.user
        obj.icerik = "İçerik kısmını otomatik atadık." ##Bu şekilde model kaydedilirken istediğimiz alana müdahale edebiliriz.
        obj.save()

delete_model(self, request, obj)
save_model() metodunun tersi olarak modelden eleman silinirken olmasını istediklerinizi bu model içerisine yazabilirsiniz.
get_ordering(self, request):
Sıralama işleminin neye göre yapılacağının dönmesini bekler. Dönen değer liste veya demet olmalıdır.
class YazilarAdmin(admin.ModelAdmin):
    list_display = ['baslik','icerik']
    def get_ordering(self, request):
        if request.user.is_superuser:
            return ['baslik','icerik']
        else:
            return ['icerik']
Burada eğer kullanıcı süper ise, sıralama ile başlık için sonra içerik için yapılacaktır. Eğer kullanıcı süper değil ise, sıralama sadece içerik için yapılacaktır.
get_readonly_fields(self, request, obj=None):
Model alanlarından sadece okunabilir alanları geriye döndürür. Dönen değer liste veya demet olmalıdır.
    def get_readonly_fields(self, request, obj=None):
        return ['id']
Not: Bu kullandığımız fonksiyon ModelAdmin seçeneklerinden readonly_fields seçeneği ile aynı işlemleri yapmaktalar. Ama gördüğünüz gibi bunlar birer fonksiyon oldukları için istediğimiz kontrolleri yaparak bu kontrollere göre sonuçlar alabilmekteyiz. Fakat bu işlemleri ModelAdmin seçenekleri ile yaptığımız zaman yaptığımız işlem tüm hepsi için geçerli olmaktadır. Yani kontrol koyma imkanına sahip olamıyoruz.
get_list_display(self, request):
list_display seçeneği ile aynı işlevi görmektedir.
    def get_list_display(self, request):
        return ['baslik','icerik','kategori']
get_list_display_links(self, request, list_display):
list_display_links seçeneği ile aynı işlevi görmektedir.
    def get_list_display_links(self, request, list_display):
        return ['baslik','icerik',]
get_urls(self):
Metodunu kullanarak ModelAdmin içerisinde url tanımlaması yaparak bu url'i görünüme yönlendirebiliriz.
from django.conf.urls import patterns
from django.shortcuts import render_to_response
class YazilarAdmin(admin.ModelAdmin):
    def get_urls(self):
        urls = super(YazilarAdmin, self).get_urls()
        my_urls = patterns('',
            (r'^gorunum/$', self.admin_site.admin_view(self.gorunum))
        )
        return my_urls + urls

    def gorunum(self, request):
        return render_to_response('gorunum.html')
Oluşturduğumuz url'e tarayıcıdan şu şekilde ulaşılır: http://localhost:8000/admin/uygulamaadi/modeladi/gorunum/
get_form(self, request, obj=None, **kwargs)
Form üzerinde değişiklik yapabilmemizi sağlar.
    def get_form(self, request, obj=None, **kwargs):
        self.exclude = ['baslik_slug']
        return super(YazilarAdmin, self).get_form(request, obj, **kwargs)
Burada form içerisindeki baslik_slug alanını gizlemiş olduk. Bunun dışında doğrudan kendi oluşturmuş olduğumuz formu da kullanabiliriz.
from django.contrib.auth.forms import *
class YazilarFormu(forms.ModelForm):
    class Meta:
        model = Yazilar
        fields = ['baslik']


class YazilarAdmin(admin.ModelAdmin):
    def get_list_display(self, request):
        return ['baslik','icerik','kategori']

    def get_form(self, request, obj=None, **kwargs):
        return YazilarFormu
has_add_permission(self, request):
class YazilarAdmin(admin.ModelAdmin):
    def has_add_permission(self, request):
        if not request.user.is_superuser:
            return False
        return True
Ekleme iznini belirler. True veya False döndürür. Eğer False döndürür ise model için ekleme yapılamaz. Burada eğer kullanıcı süper kullanıcı değilse ekleme yapamaz.
has_change_permission(self, request, obj=None)
has_add_permission gibi çalışır. Eğer kullanıcının model üzerinde değiştirme yetkisi verilmemiş ise model sayfasını göremez.
class YazilarAdmin(admin.ModelAdmin):
    list_display = ['baslik']
    def has_change_permission(self, request, obj=None):
        if not request.user.is_superuser:
            return False
        return True
has_delete_permission(self, request, obj=None)
Ekleme ve değiştirmeden bir farkı yoktur. Eğer False döndürülür ise model elemanları silinemez olur. queryset(self, request)
Model üzerinde sorgu çalıştırabilmemizi sağlar. Örneğin aşağıdaki method eğer kullanıcı süper değil ise sadece kendi yazılarını görebilmesini sağlıyor.
class YazilarAdmin(admin.ModelAdmin):
    def queryset(self, request):
        qs = super(YazilarAdmin, self).queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(yazar=request.user)
Burada ki qs içerisinde tüm yazılar bulunmaktadır. Bu nesne üzerinden bu yazılar üzerinde sorgular çalıştırılabilir. Örneğin tüm yazıların tarihlerini güncelleyen şöyle bir fonksiyon yazılabilir.
import datetime
class YazilarAdmin(admin.ModelAdmin):
    def queryset(self, request):
        qs = super(YazilarAdmin, self).queryset(request)
        for q in qs:
            q.tarih =datetime.datetime.now()
            q.save()
        return qs
Kaynakça

18 Temmuz 2014 Cuma

ModelAdmin Seçenekleri

Bu yazı serimizde Django'nun bize sağlamış olduğu yönetim panelini özelleştirmekten bahsedeceğiz. Django ile birlikte gelen güçlü bir yönetim paneli bulunmaktadır. Ama işin asıl güzel kısmı bu yönetim panelini isteklerimiz doğrultusunda şekillendire biliyoruz.
date_hierarchy
Elimizde bulunan bir modelin listelemesi yapılırken tarih bazlı filtreleme yapılması isteniyorsa bu seçenek kullanılabilir. Elimizde şöyle bir model olduğunu düşünelim.
class Kisi(models.Model):
    isim = models.CharField(max_length=100)
    dogum_tarihi  = models.DateField()

    def __unicode__(self):
        return self.isim
Şimdi admin.py dosyasına şu kodları yazıyoruz.
class KisiAdmin(admin.ModelAdmin):
    date_hierarchy = 'dogum_tarihi'

admin.site.register(Kisi,KisiAdmin)
Bu ayarlamaları yaptıktan sonra yönetim panelinde Kisi modeline girerseniz en üstte girdiğiniz tarihleri görebilirsiniz. Bu tarihlere tıklayarak sadece o tarihlere ait kişileri listeleye bilirsiniz.
Resimde 1992 ve 1993 sayılarını görebiliyorsunuz.
ordering
ordering seçeneği ile listeleme yapılırken hangi alana göre sıralama yapılacağını seçebiliriz. Listeleme için ordering seçeneğini de ekleyelim.
class KisiAdmin(admin.ModelAdmin):
    date_hierarchy = 'dogum_tarihi'
    ordering = ('isim', )
Ali ve Veli'nin yerlerinin değiştiğini görebilirsiniz.
actions
Listelenen elemanları seçerek toplu bir şekilde işlem yapabiliyoruz. Django'da varsayılan olarak sadece "Seçilileri sil" seçeneği gelmektedir. Ama biz seçilen elemanlar üzerinde başka işlemler yaptırmak isteyebiliriz. Bunun için actions seçeneğini kullanacağız. Çok mantıklı olmasa da göstermek açısından kullanıcı doğum tarihlerini günümüz tarihi olarak güncelleyen bir seçenek ekleyelim.
import time
def yil_duzenle(modeladmin,request,queryset):
    queryset.update(dogum_tarihi=time.strftime("%Y-%m-%d"))
yil_duzenle.short_description = 'Doğum tarihlerini güncelle'

class KisiAdmin(admin.ModelAdmin):
    actions = [yil_duzenle]

admin.site.register(Kisi,KisiAdmin)
Kodlaru bu şekilde düzenledikten sonra seçenekler listesinde "Doğum tarihlerini güncelle" seçeneğini şu şekilde göreceksiniz.
Seçtiğiniz kullanıcıların doğum tarihleri güncellenecektir.
actions_on_top & actions_on_bottom
Seçenekler kutusunun nerede duracağına karar vermemizi sağlar. Varsayılan olarak şöyle gelir.
    actions_on_bottom = False
    actions_on_top = True
Eğer her ikisi de True yapılırsa hem listenin üstünde hemde altında gözükecektir.
actions_selection_counter
Yukarıda ki resmi incelerseniz "İki nesne arasından seçim.." diye bir alan göreceksiniz. Burada seçili olan nesnelerin sayısı hakkında bilgi verilmektedir. Bu actions_selection_counter seçeneği ile yapılmaktadır. Varsayılan olarak True gelmektedir. Eğer görünmesini istemiyorsak False yapabiliriz.
class KisiAdmin(admin.ModelAdmin):
    actions = [yil_duzenle]
    actions_selection_counter = False

fields & exlude
Modelden ekleme işlemi yaparken fields ile gösterilecek alanları exlude ile ise gösterilmeyecek alanları belirleyebiliyoruz. Örneğin elimizde şöyle bir model olduğunu düşünelim.
class Yazi(models.Model):
    baslik  = models.CharField(max_length=100)
    tarih   = models.DateTimeField()
    taslak  = models.BooleanField(default=False)
Burada yazi ekleme işlemi yapılırken sadece "baslik" be "tarih" alanlarının görüntülenmesini istersek;
class YaziAdmin(admin.ModelAdmin):
    fields = ('baslik', 'tarih')

admin.site.register(Yazi,YaziAdmin)
Aynı işlemi exlude ile de yapabiliriz. Göstermek istemediğimiz alan "taslak" olduğu için onu yazmalıyız.
class YaziAdmin(admin.ModelAdmin):
    exclude = ('taslak', )
Aynı işlemi görecektir. Eğer iki alanın aynı satırda gözükmesini istersek fields özelliği içerisinde alanları demet içerisinde yazabiliriz.
class YaziAdmin(admin.ModelAdmin):
    fields = (('baslik', 'taslak'), 'tarih', )

fieldsets
Alan ekleme sayfasını parçalamak istersek bu özelliği kullanabiliriz. Örneğin eklem sayfasında zorunlu alanlar ile zorunlu olmayan alanları altlı üstlü iki parça olarak göstermek istersek şöyle bir kullanım işimizi görecektir. Bir önceki modelden devam edelim.
class YaziAdmin(admin.ModelAdmin):
    fieldsets = (
        ('Mecburi Alanlar',{
            'fields':('baslik', 'tarih')
        }),
        ('Seçmeli Alanlar',{
            'classes': ('collapse', ),
            'fields' : ('taslak', ),
        })
    )
Burada sayfayı ikiye böldük. Demetin ilk elemanıda bir demet ve bu demetin ilk elamanı ise bölümün başlığını içeriyor. Burada dikkat ettiyseniz "Seçmeli Alanlar" kısmında classes diye bir alan kullandık. Bu bölüme stil atamamızı sağlıyor. Burada atadığımız stil Django'nun kendisinde bulunan collapse. Bu stil sayesinde bölüme Göster/Gizle butonu gelecektir.

css & js
Eğer istersek model sayfasında yüklenecek css ve js dosyalarının tanımını yapabiliriz.
class YaziAdmin(admin.ModelAdmin):
    class Media:
        css = {
            "all":("style.css", )
        }
        js = ("code.js", )
Buradaki style.css ve code.js dosyaları "static" dizini içerisinde aranır. Yani /static/ linkine hangi dizini tanımladıysak o dosya içerisinde arama yapılır.
filter_horizontal
Model içerisinde bulunan ManyToManyField alanları için gelişmiş seçim alanı sunar. Bu alanı Django'nun User modeli gibi alanlarda görmüştük. Bu seçenek seçenek sadece ManyToManyField alanlar için kullanılabilir.
class YaziAdmin(admin.ModelAdmin):
    filter_horizontal = ('kategori', )

filter_vertical
filter_horizontol ile aynı işlemi yapmaktadır. Sadece seçim alanları dikey gözükmektedir.
forms
Django yönetim panelindeki modele ekleme yaparken ekleme işleminin oluşturduğumuz bir forma göre yapılmasını istersek bu seçeneği kullanabiliriz. Örneğin oluşturduğumuz Yazi modeline ekleme yapılırken başlığının tekrarlanmasını engelleyen bir form oluşturalım ve modele bu formu gösterelim.
class YaziForm(forms.ModelForm):
    def clean_baslik(self):
        baslik = self.cleaned_data.get('baslik')
        if Yazi.objects.filter(baslik=baslik).exclude(id=self.instance.id).count():
            raise forms.ValidationError('Bu başlıkta bir yazı bulunmaktadır.')
        return baslik
class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik', )
    filter_vertical = ('kategori', )
    form = YaziForm
admin.site.register(Yazi, YaziAdmin)
Burada bir formu oluşturduk ve bu formu YaziAdmin içerisinde gösterdik. Artık bir yazı kaydedilirken bu form dikkate alınacak ve aynı başlıktan birden fazla yazı kaydedilmeyecektir.

formfield_ovverides
Form alanlarını özelleştirmek için kullanabiliriz. Örneğin elimizdeki yazı modeline içerik kısmı ekleyelim.
class Yazi(models.Model):
    baslik  = models.CharField(max_length=100)
    icerik = models.TextField()
    tarih   = models.DateTimeField()
    taslak  = models.BooleanField(default=False)
    kategori = models.ManyToManyField(Kategori)
Bu modeldeki TextField alanları için CKEditor kullanmak istediğimizi düşünürsek bu özellik bizim işimizi görebilir.
from django.db import models
from ckeditor.widgets import CKEditorWidget

class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik', )
    filter_vertical = ('kategori', )
    form = YaziForm
    formfield_overrides = {
        models.TextField : {'widget':CKEditorWidget}
    }

admin.site.register(Yazi, YaziAdmin)
Formda bulunan tüm TextField alanları artık CKEditor olacaktır. İşte bu tarz özelleştirmeler için kullanbileceğimiz bir özelliktir.
CKEditör'ün Django'da nasıl kullanıldığı bu yazının konusu olmadığı için anlatılmayacaktır. Kullanımına buradan bakabilirsiniz.
Veya şöyle kullanarak Model içerisindeli CharField alanlarına class ataması yapabilirsiniz.
class YaziAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.TextField : {'widget':CKEditorWidget},
        models.CharField : {'widget':forms.TextInput(attrs={'class':'sinif'})}
    }

inlines
inlenes seçeneği ile Ebeveyn Model'e ait olan alt modelleri aynı sayfada düzenleyebilmemiz olanağını verir. Yani bir yazar eklerken aynı zamanda Yazar'a ait kitapları da ekleyebiliriz. Alanın ForeingKey olması gerekir. Elimizde şöyle iki model olduğunu düşünelim.
class Yazar(models.Model):
    isim = models.CharField(max_length=100)

class Kitap(models.Model):
    yazar = models.ForeignKey(Yazar)
    baslik = models.CharField(max_length=100)
    tarih = models.DateField()
Şimdi yazar ekleme veya düzenleme işlemi yaparken aynı zamanda yazara ait kitapları da düzenleyebilmemizi sağlayacak inlines tanımlamasını yapalım.
class KitapInline(admin.StackedInline):
    model = Kitap

class YazarAdmin(admin.ModelAdmin):
     inlines = [KitapInline]

admin.site.register(Yazar, YazarAdmin)
Burada KitapInline içerisinde hangi modeli kullanacağını belirtiyoruz. YazarAdmin içerisinde ise oluşturduğumuz inline tanımlamasını gösteriyoruz.
Inline tanımlaması yaparken admin.StackedInline'ı kullandık eğer isterseniz admin.TabularInline tanımlasını kullanabilirsiniz. Tek fark olarak Kitap modeli içerisindeki alanlar yan yana görünecektir.
list_display
Model elemanları listelenirken hangi elemanların gösterileceğini belirlememizi sağlar. Örneğin şöyle bir kullanım yapabiliriz.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', 'tarih']
Kitabın yazarını almak için şöyle bir yol izleyebiliriz.
def yazari(obj):
    return  obj.yazar.isim
yazari.short_description = 'Kitap Yazarı'

class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', yazari, 'tarih']
veya,
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', 'yazari', 'tarih']

    def yazari(self, obj):
        return  obj.yazar.isim
    yazari.short_description = 'Kitap Yazarı'
Eğer göstermek istediğimiz alan ForeignKey bir alan ise Model içerisindeki __unicode__() fonksiyonu çağrılır. Bizim yukarıda yaptığımız işlemi şu şekilde de yapabiliriz.
class Yazar(models.Model):
    isim = models.CharField(max_length=100)

    def __unicode__(self):
        return self.isim

class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', 'yazar', 'tarih']
list_display ManyToManyField alanlarını desteklemez. Bu alanları göstermek için yine özel fonksiyon yazmalıyız. Önceki Kitap Yazar arasıdanki ForeignKey ilişkisini ManyToManyField haline getirelim.
def yazarlari(obj):
    return ",".join(yazar.isim for yazar in obj.yazar.all())
yazarlari.short_description="Kitap Yazarları"

class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', yazarlari, 'tarih']
ManyToManyField alanları göstermek için ise bu şekilde bir yöntem kullanabiliriz.
Eğer alan BooleanField veya NullBooelanField ise list_display ile gösterilirken Django'nun iconları ile gösterilirler. Daha önce oluştmuş olduğumuz Yazi modeli üzerinden gösterelim.
class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik','taslak' )
Eğer isterse alanlar gösterilirken html tagleri kullanılabilir ve stil verilebilir. Örneğin kitap isimlerini renkli gösterelim.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['stil_baslik', yazarlari, 'tarih']

    def stil_baslik(self,obj):
        return '%s'%obj.baslik
    stil_baslik.allow_tags = True
Herhangi bir fonksiyon yazabilir ve bu fonksiyonun dönüşüne göre Django'nun Boolean alan ikonlarını gösterebiliriz.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['stil_baslik', yazarlari, 'yil']

    def yil(self,obj):
        return int(obj.tarih.strftime('%Y')) < 2000
    yil.boolean = True
Eğer kitap 2000 yılından önceye ait ise Django'nun True ikonu gösterilecektir.
Örneğin kitap yazarlarını gösterirken yazara tıkladığında düzenleme sayfasına yönlendiren bir fonksiyon yazalım. Böylece her yazar düzenleme sayfasına giden bir link bağlamış olacağız.
def yazarlari(obj):
    yazarlar=""
    for yazar in obj.yazar.all():
        yazarlar = yazarlar + ''+yazar.isim+''+" "
    return  yazarlar
yazarlari.allow_tags = True
yazarlari.short_description="Kitap Yazarları"
Burada site adını veri tabanından çekmek daha doğru olacaktır.
list_display_links
Model elemanları listelenirken değiştirme sayfasına gidilmesi için varsayılan olarak list_display ile belirtilen ilk alana link bağlanmıştır. Eğer bunun değiştirilmesi isteniyorsa kullanılır.
    list_display = ['stil_baslik', yazarlari, 'yil']
    list_display_links = ['stil_baslik',yazarlari]
Not : list_display_links içerisinde tanımlanan her alanın list_display içerisinde tanımlanmış olması gerekir.
list_editable
Model elemanları listelenirken aynı zamanda düzenlenebilme özelliği sağlar. Bu şekilde örneğin text içeriği olan bir eleman text input'una dönüşür ve eleman düzenlenebilir.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', yazarlari, 'tarih']
    list_display_links = [yazarlari]
    list_editable = ['baslik','tarih']
Not : list_editable içerisinde tanımlanmış alanın list_display içerisinde tanımlanmış list_display_links içerisinde tanımlanmamış olması gerekir.
list_filter
list_filter listeleme yapılırken sağ tarafta filtreleme alanının çıkmasını sağlar. Bu alana Django'nun Kullanıcılar modelinden aşınayız zaten.
Bu şekilde bir alan çıkmasını sağlar. Örneğin daha önce oluşturmuş olduğumuz Yazi modelinde taslak alanı bulunmaktadır. Buradaki taslak alanına göre filtreleme yaptırabiliriz.
class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik','taslak', )
    list_filter = ('taslak', 'kategori', )
list_filter'a yazılan alanlar BooleanField, CharField, DateField, DateTimeField, IntegerField, ForeignKey veya ManyToManyField olabilir. Bizim yaptığımız örnekte taslak alanı BooleanField iken kategori alanı ManyToManyField. Şu şekilde görünecektir.
Filtreleme için kendi fonksiyonlarınızı yazabilirsiniz. Yazdığınız fonksiyona göre filtreleme olacaktır. Bunun için Django'nun kendi sitesinde çok güzel bir örnek bulunmaktadır. Buradan ulaşarak inceleyebilirsiniz.
list_per_page
Sayfalama yapılarak her sayfada kaç eleman gösterileceğini belirtir. Varsayılan olarak 100 gelmektedir.
class YaziAdmin(admin.ModelAdmin):
    list_per_page = 1

prepopulated_fields
slug veya başka söyleyiş ile sef_link alanı oluşturmak için kullanılır. slug kısaca herhangi bir textin tarayıcı url'ine uygun haline getirilmiş halidir. Örneğin;
Text = bu bir yazıdır.
Slug = bu-bir-yazidir
Yazi modelimizi baslik_slug alanı ekledik.
class Yazi(models.Model):
    baslik  = models.CharField(max_length=100)
    baslik_slug = models.CharField(max_length=100)
    ...
Bu özelliği kullandığımız zaman baslik alanı doldurulurken bir Javascript kodu çalışacak ve eş zamanlı olarak baslik_slug alanı otomatik olarak doldurulacaktır.
class YaziAdmin(admin.ModelAdmin):
    prepopulated_fields = {
        'baslik_slug':('baslik',)
    }

radio_fields
ForeignKey bir alan seçilirken default olarak bir seçim kutusundan seçim yapılır. Eğer bir böyle olmasını değil de seçim düğmeleri ile seçim yapılmasını istersek bu özelliği kullanabiliriz. Daha önceki Yazar,Kitap modellerini hatırlayalım.
class Yazar(models.Model):
    isim = models.CharField(max_length=100)

    def __unicode__(self):
        return self.isim

class Kitap(models.Model):
    yazar = models.ForeignKey(Yazar)
    baslik = models.CharField(max_length=100)
    tarih = models.DateField()
Şimdi buradaki Kitap içerisindeki ForeignKey için bu özelliği uygulayalım.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik','yazar', 'tarih']
    radio_fields = {'yazar':admin.HORIZONTAL}
Buradaki HORIZONTAL yerine VERTICAL kullanabilirsiniz. Tek fark olarak seçim düğmeleri alt alta görünecektir.
raw_id_fields
Az önce belirttiğimiz gibi seçim alanı olarak bir seçim kutusu gelir. Bu seçim kutusunu seçim düğmelerine çevirdik. Bu seçenek ise seçim yapma işlemini id üzerinden yapılmasını sağlar. Örnek üzerinden gösterelim. Daha önceki Yazı modeli içerisindeki kategori kısmını bu seçenek içerisinde kullanalım.
class YaziAdmin2(admin.ModelAdmin):
    raw_id_fields = ['kategori']
raw_id_fields ManyToManyFields ve ForeignKey alanları için kullanılabilir.
readonly_fields
Alanları sadece okunabilir yapmak için kullanılır. Buraya girilen alanlara müdahale edilemez. Örnek olarak Kitap modelinde id alanını gösterelim ama kullanıcı müdahale edemesin.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik','yazar', 'tarih']
    readonly_fields = ['id']


save_as
Varsayılan olarak False gelir. Bir modele eleman eklenirken veya düzenlenirken kaydet kısmında üç buton bulunur. Bunlar "Kaydet", "Kaydet ve düzenlemeye devam et" ve "Kaydet ve yenisini ekle". Eğer bu seçenek True yapılırsa var olan bir eleman düzenlenirken "Kaydet ve yenisini ekle" butonu "Yeni olarak kaydet" haline gelir. Bu butona tıklandığında ise yapılan değişiklikler yeni eleman olarak kaydedilir ve eskisi de kalır.
class KitapAdmin(admin.ModelAdmin):
    save_as = True

save_on_top
Varsayılan olarak False gelir. Bahsettiğimiz bu üç buton sayfanın altında bulunur. Eğer sayfanın üstünde de bulunması istenirse bu seçenek kullanılır.
class KitapAdmin(admin.ModelAdmin):
    save_on_top = True

search_field
Model elemanlarının listeleme sayfasına arama formu eklenmesini sağlar. Arama işlemi yapılırken search_field ile belirtilen listedeki sütunlar baz alınarak yapılır.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['id','baslik','yazar', 'tarih']
    search_fields = ['baslik','yazar__isim']
Bu aramanın sorgusu şu şekildedir;
WHERE (baslik ILIKE '%dijital%' OR yazar__isim ILIKE '%dijital%')
AND (baslik ILIKE '%brown%' OR yazar__isim ILIKE '%brown%')
Sorgu içerisindeki like ifadesinde '%ifade%' kullanımı dikkatinizi çekmiştir. Eğer 'ifade%' veya '%ifade' şeklinde olmasını istersek ^ ifadesini kullanmalıyız.
class KitapAdmin(admin.ModelAdmin):
    search_fields = ['^baslik','^yazar__isim']
Sorgunun yeni hali şöyledir;
WHERE (baslik ILIKE 'dijital%' OR yazar__isim ILIKE 'dijital%')
AND (baslik ILIKE 'brown%' OR yazar__isim ILIKE 'brown%')
Eğer tam eşleşme yapmak istersek = ifadesini kullanmalıyız.
class KitapAdmin(admin.ModelAdmin):
    search_fields = ['=baslik','=yazar__isim']
Sorgunun yeni hali şöyledir;
WHERE (baslik ILIKE 'dijital' OR yazar__isim ILIKE 'dijital')
AND (baslik ILIKE 'brown' OR yazar__isim ILIKE 'brown')
Sorgulardaki ManyToManyField alanı olan yazar için sorgu bu şekilde değildir. Sadece göstermek amacı ile bu şekilde kullandık. Kaynakça

14 Temmuz 2014 Pazartesi

Hata Raporlama

Django'da bir proje oluşturduğumuz o zaman varsayılan olarak hata ayıklama(DEBUG) kipi açık olur. Bu biz kodlarımızı yazarken yazım hatalarını bulabilmemizi sağlaması içindir. Yani var olan hata ile ilgili Django bizi bilgilendirir. Biz de bu bilgilendirme sayesinde nerede hata yaptığımız anlayabiliriz. Ama yaptığımız projeyi yayınlarken bu şekilde olması güvenlik açısından problem teşkil etmektedir. Bu şekilde kullanıcılar projemiz hakkında bilgi sahibi olabilir, yazdığımız kodların bir kısmını görebilirler. Kullanıcılar bu bilgileri kullanarak istemediğimiz sonuçlara sebep olabilir. Bu yüzden hata ayıklama kipini kapatıyoruz. Bunun için settings.py dosyası içerisindeki DEBUG değişkeninin değerini False yapıyoruz.
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
Gördüğünüz gibi Django da bize hata ayıklama kipi açıkken projemizi yayınlamamız konusunda güvenlik uyarısı veriyor. Bu ayarı yaptıktan sonra projenizi başlatmak isterseniz şöyle bir hata alacaksınız.
$ python manage.py runserver
CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.
Django bizi yine uyarıyor. Proje için izinli hostları belirtmemiz gerekiyor. Bunun yüzden settings.py dosyası içerisinde ALLOWED_HOSTS değişkenini içerisinde izinleri vermeliyiz.
ALLOWED_HOSTS = ['*']
İster bu şekilde tüm host'lara izin verebilirsiniz. Veya sadece kendi belirlediklerinizi izin verebilirsiniz.
ALLOWED_HOSTS = ['127.0.0.1','localhost']
Güvenlik açısından tavsiye edilen yöntem budur. Bunu yaptıktan sonra herhangi bir sayfayı açmaya çalıştığınızda style dosyalarının yüklenmediğini göreceksiniz. Bu ayarlamaları yaptıktan sonra statik (CSS dosyaları da dahil) dosyaların yüklenebilmesi için projemizi --insecure parametresi ile çalıştırmalıyız.
python manage.py runserver --insecure
Sunucu Hataları (500)
Sunucudan kaynaklanan hatalar. Projeyi geliştirirken yazdığımız kodlarda bir hata oluşursa Django bunu tarayıcıda bize basıyordu. Bu şekilde hata ile ilgili bizi bilgilendiriyordu. Örneğin;
Bu durum DEBUG = True olduğu durum için geçerlidir. Biz projemizde False yaptığımız için ekrana bu bilgi basılmayacaktır. Onun yerine ekranda sadece Server Error (500) yazar. Buradan da anlayabileceğiniz gibi hatanın kodu 500'dür. Yani aslında kullanıcıya 500.html sayfası gösterilir ve ardından settings.py dosyasında belirtilen ADMINS kullanıcılarına hata e-posta olarak gönderilir.
Şimdi settings.py dosyasında ADMINS değişkenine yönetici kullanıcıları belirtiyoruz.
ADMINS = (
    ('Mazlum Agar','mazlum.agar@gmail.com'),
)
Django tarafından gönderilen postaların kimin tarfından gönderilmesini istiyorsak SERVER_EMAIL değişkenine belirtiyoruz.
SERVER_EMAIL = 'Django Uyari <django@mail.com>'
Artık hata oluştuğunda gösterilecek 500.html sayfasını oluşturabiliriz. Bu sayfayı oluşturduktan sonra istediğiniz bir şablon dizine atabilirsiniz. Biz hastahane/sablonlar dizini içerisine atıyoruz.
hastahane/sablonlar/500.tml
<!DOCTYPE html>
<html>
<head>
    <title>Erişilemez</title>
</head>
<body>

<h2>Teknik hata</h2>


<p>Üzgünüz,sunucuda oluşan teknik bir hatadan dolayı istediğiniz sayfa gösterilemiyor.</p>

<img src="/dosyalar/500.jpg" />
</body>
</html>
Artık kullanıcılar şöyle bir sayfa görecekler.
Sayfa Bulunamadı (404)
Projede var olmayan bir url girildiğini Django bize yukarıdaki gibi sayfanın olmadığına dair bir hata basıyordu. Bu sayfada var olan tüm urlleri gösteriliyordu. Herhangi bir kullanıcının bu urlleri görmesi tehlikeli olabilir. Normal kullanıcılar için ise bu sayfa mantıklı olmayacaktır. Bu yüzden kendi 404 sayfamızı oluşturacağız. İlk önce şablon içerisinde request nesnesini ve django yönetim panelini kullanmak istersek settings.py dosyasına şu satırları ekliyoruz.
TEMPLATE_CONTEXT_PROCESSORS = (
     #Oluşturacağımız 404 sayfasında request nesnesini kullanmak için
    'django.core.context_processors.request',
     #Yönetim panelini kullanmak için
    'django.contrib.auth.context_processors.auth'
)
Son olarak 404.html sayfasını oluşturarak aynı dosya içerisine kaydedelim.
hastahane/sablonlar/500.tml
<!DOCTYPE html>
<html>
<head>
    <title>Sayfa Bulunamadı</title>
    <link rel="stylesheet" type="text/css" href="/static/base.css" />
</head>
<body>

<img src="/static/404.jpg" width="300" height="300"/>

<h2>Sayfa Bulunamadı</h2>

<p>Aradığınız sayfa buluanamadı.</p>

<br>Erişmek istediğiniz sayfa : <b>{{ request.build_absolute_uri }}</b>

</body>
</html>
Kullanıcı olmayan bir sayfaya ulaşmaya çalıştığı zaman;
Kaynakça

10 Temmuz 2014 Perşembe

Başka URL Dosyası İçermek

Şimdiye kadar tüm url tanımlamalarını hastahane/urls.py dosyasında yaptık. Bizim yaptığımız projede bu durum önemli olmayabilir. Ama büyük projelerde uygulama sayısı arttıkça karmaşıklıkta artacaktır. Bu yüzden her uygulamanın kendi url dosyası oluşturularak. hastahane/urls.py dosyasında bu dosyalar içe aktarılabilir. Bu şekilde çok daha anlaşılır tanımlamalar yapmış olabiliriz. Şu ana kadar yaptığımız tüm görünümler yonetim/views.py dosyasında ama biz görmemiz açısından şu link tanımlamalarımızı yonetim/urls.py dosyasına taşıyalım.
Daha önceden hastahane/urls.py dosyasında tanımladığımız bazı urller.
    url(r'^yonetim/$',yonetim.views.yonetim),
    url(r'^yonetim/profil/$','yonetim.views.kullanici_profili'),
    url(r'yonetim/profil/profil_resimleri/(.*)','yonetim.views.profil_resimleri'),
Gördüğünüz gibi tüm linkler yonetim ile başlıyor. yonetim/urls.py dosyasını şu şekilde oluşturalım.
from django.conf.urls import patterns,url


urlpatterns = patterns('yonetim.views',
                        url(r'^$','yonetim'),
                        url(r'^profil/$','kullanici_profili'),
                        url(r'^profil/profil_resimleri/$','profil_resimleri'),
                       )
Burada yonetim kısmını yazmadığımıza dikkat edin. Onu hastahane/urls.py dosyasında belirteceğiz. Görünümleri yüklerken yonetim.views.yonetim şeklinde yazmamıza da gerek kalmadı. patterns() fonksiyonuna verdiğimiz ilk parametre olan yonetim.views tüm görünümlerin bu dosya içinde aranacağını belirtiyor.
Son olarak yonetim/urls.py dosyasını hastahane/urls.py dosyasında içe aktaralım.
url(r'^yonetim/',include('yonetim.urls')),
Kaynakça

7 Temmuz 2014 Pazartesi

Kullanıcı Profili

Projemizde kullanıcılarımız bulunmaktadır. Fakat bu kullanıcıların herhangi bir profilleri yok. Bu bölümde bunu yapalım. Her kullanıcının bir profili olsun. Bu profilde resim,telefon,şehir gibi bilgilerini alalım. Django'da resim yani ImageField alanını kullanabilmek için PIL kütüphanesinin kurulu olması gereklidir. Bu kütüphanenin nasıl kurulacağını CAPTCHA Kullanımı yazısında anlatmıştık. Django'da yüklenecek dosyaların yüklenebilmesi için settings.py dosyasında MEDIA_ROOT değişkeninde yüklenecek yerin belirtilmesi gereklidir. Hatırlarsanız daha önceden statik dosyaları tuttuğumuz statik_dosyalar dizinimiz vardı. Resimleri de bu dizin içerisinde resimler adında oluşturacağımız bir dizinde saklayacağız. Bunun için hastahane/settings.py dosyasına şu satırı ekliyoruz.
MEDIA_ROOT = os.path.join(anadizin,'statik_dosyalar/resimler')
Django'da resimler yüklendikten sonra nesne üzerinde bir değişiklik olduğunda veya silindiğinde bulunduğu dizinde kalmaktadır. Bu da proje için gereksiz yük demektir. Ya bu şekilde kalan resimleri silme işlemini kendimiz yapacağız yada Django'nun bize sağlamış olduğu django-cleanup modülünü kullanacağız. İkinci yol daha mantıklı olduğu için biz de bu yolu seçeceğiz. django-cleanup modülünü http://github.com/un1t/django-cleanup adresinden indirebilirsiniz. İndirdiğiniz dosyayı açtıktan sonra bu dizine girerek şu komutu vermeniz gereklidir.
python setup.py install
Windows kullananlar için;
pythonunkuruluoldugudizin/python.exe setup.py install
şeklinde olacağını söylememize gerek yok sanırız. Modülü kurduğumuza göre artık projemize dahil edebiliriz. Bunun için hastahen/settings.py dosyasındaki INSTALLED_APPS değişkeninin her zaman en sonunda olacak şekilde şu satırı ekliyoruz.
'django_cleanup',
Artık kullanıcı profiline ait modeli tanımlayabiliriz. yonetim/models.py dosyasına yazıyoruz.
import os

def resim_patikasi_belirle(instance,dosyaadi):
    uzanti = os.path.splitext(dosyaadi)[-1]
    dosyanin_kayit_adi = os.path.join('profil_resimleri',str(instance.user.id)+uzanti)
    return dosyanin_kayit_adi

class KullaniciProfili(models.Model):
    user = models.OneToOneField(User,unique=True,related_name='KullaniciProfili')
    resim = models.ImageField(upload_to=resim_patikasi_belirle)
    sehir = models.CharField(max_length=30,blank=True)
    telefon = models.CharField(max_length=11,blank=True)
    okul = models.CharField(max_length=40,blank=True)
Modeldeki user satırında OneToOneField alanını kullandığımıza dikkat edin. Bu alan sayesinde bu modeldeki her veri bir kullanıcı ile ilişkili olacaktır. Profil modelinin hangi kullanıcılara uygulanacağını related_name özelliği ile belirtiyoruz. Eğer bir profilin tüm kullanıcılara uygulanmasını istiyorsanız related_name argümanı yerine,hastahane/settings.py dosyasına şu satırı ekleyebilirsiniz.
AUTH_PROFILE_MODULE = 'yonetim.KullaniciProfili'
Bu yöntemi kullanmıyoruz. Bizim kullandığımız yöntem sayesinde birden fazla kullanıcı profili oluşturabiliriz.
Dosya içerisinde resmin yükleneceği yeri belirleyen bir işlev tanımladık. Bu işlev iki iki parametre almaktadır. instance parametresi sayesinde yüklenen resmin hangi kullanıcı tarafından yüklendiğini öğrenebiliriz. instance.user nesnesi kullanıcı ile ilgili her bilgiyi barındırır. dosyaadi ise yüklenen dosyanın adıdır. Dosyanın adını alarak uzantısını elde etmiş oluyoruz. Burada işlev dosyanın kayıt edileceği yer + kullanıcı numarası + resim uzantısı şeklinde resmi geri döndürüyor. Yani 10 numaralı kullanıcı jpg türünden bir resim yüklediğinde statik_dosyalar/resimler/profil_resimleri/10.jpg şeklinde resim kayıt edilecektir.
Oluşturduğumuz modeli kullanarak formu oluşturalım. yonetim/forms.py dosyasına ekliyoruz.
from django.db.models.signals import post_save

class KullaniciProfiliFormu(forms.ModelForm):
    class Meta:
        model = KullaniciProfili
        exclude = ('user',)

def kullanici_profili_olustur(sender,instance,created,**kwargs):
    if created:
        KullaniciProfili.objects.create(user=instance)

post_save.connect(kullanici_profili_olustur,sender=User)
İlk önce KullaniciProfili modelini kullanarak formu oluşturduk. Buradaki kullanici_profili_olustur işlevi her kullanıcı için KullanıcıProfili tablosundan kullanıcı için bir satır oluşturacaktır. Buradaki form kaydedildiğinde post_save sinyali yayımlanır. Bu sinyali ise post_save.connect işlevi sayesinde yakalıyoruz. Bu işleve şunu diyoruz: Form kayıt sinyalini yakaladığın anda kullanici_profili_olustur() işlevini çağır. Bu işlev çağrılır ve kullanıcı için bir satır oluşturulur. Eğer bir kullanıcı bu şekilde oluşturulmamış ise profili oluşturulamayacaktır. Bunun kontrolünü görünüm içerisinde yapacağız.
yonetim/views.py dosyasına ekliyoruz.
def kullanici_profili(request):
    from models import KullaniciProfili
    if not KullaniciProfili.objects.filter(user=request.user.id):
        KullaniciProfili.objects.create(user=request.user)

    if request.method == "POST":
        form = KullaniciProfiliFormu(request.POST,request.FILES,instance=request.user.KullaniciProfili)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/yonetim/')
    else:
        form = KullaniciProfiliFormu(instance=request.user.KullaniciProfili)
    return render_to_response('kullanici_profili.html',locals(),context_instance=RequestContext(request))
Az önce görünüm içerisinde kontrolünü yapacağımızı söylemiştik. 2,3,4. satırlarda bu kontrolü yapıyoruz. Eğer kullanıcı bu şekilde oluşturulmamış ise KullaniciProfili tablosunda o kullanıcıya satır oluşturulamamış demektir. Biz de oluşturma işlemini burada yapıyoruz.
3.satır: Eğer kullanıcı için ZiyaretciProfili tablosunda satır oluşturulmamışsa
4.satır: Bu kullanıcıya satır oluştur.
Son olarak bu görünüm için şablonumuzu oluşturalım. /yonetim/sablonlar/kullanici_profili.html oluşturuyoruz ve ekliyoruz.
<!DOCTYPE html>
<html>
<head>
    <title>Kullanıcı Profili</title>
    <link rel="stylesheet" type="text/css" href="/dosyalar/style.css" />
</head>
<body>

<h1>Profilim</h1>


<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <table border="1">
        {{ form }}
    </table>
    <input type="submit" value="Profil Kayıt" />
</form>


</body>
</html>
Şablonu da oluşturduğumuza göre url tanımlamamızı yapabiliriz. hastahane/urls.py dosyasına ekliyoruz.
url(r'profil/profil_resimleri/(.*)','yonetim.views.profil_resimleri'),
Her şey tamam yapmamız gereken tek şey oluşturduğumuz model için tabloların oluşmasını sağlamak. Bunun için komut satırında şu komutu veriyoruz.
python manage.py syncdb
Daha önce oluşturduğumuz yonetim_giris.html şablonuna şu linki ekliyoruz.
<div><a href="/profil/"/>Profilim</div>
Şimdi sunucuyu yeniden başlatın ve bir kullanıcı ile giriş yaptıktan sonra. Eklediğimiz linke tıklayın veya tarayıcıdan http://localhost/profil adresine gidin.
Bu pencerenin boş hali ile karşılaşacaksınız. Kullanıcı için bu bilgileri doldurduğunu zaman ise bu pencere gelecek karşınıza. Dikkat ederseniz var olan resim için bir link verilmiş. Fakat bu link şu anda çalışmayacaktır. Çünkü bu link ile ilgili görünümü yazmadık. Şimdi onu yazalım.
yonetim/views.py dosyasına ekliyoruz.
from django.conf import settings
def profil_resimleri(request,resim_adi):
    dosya_yolu = os.path.join(settings.MEDIA_ROOT,'profil_resimleri',resim_adi)
    if os.path.join(dosya_yolu):
        resim = os.path.join(settings.MEDIA_ROOT,'profil_resimleri',resim_adi)
        uzanti = os.path.splitext(resim_adi)[-1][1:]
        mime_tipi='image/%s' % uzanti
        resim_bilgi = open(resim,"rb").read()
        return HttpResponse(resim_bilgi,mimetype=mime_tipi)
    else:
        resim_bilgi = open(os.path.join(settings.MEDIA_ROOT,'resim_yok.png'),"rb").read()
        return HttpResponse(resim_bilgi,mimetype='image/png')
Görünümde resim_adi ile tarayıcıdan gelen resmin adını alıyoruz. Ve bu resmin yolunu dosya_yolu değişkenine atıyoruz. Eğer bu resim var ise resmi yok ise de resim_yok.png'i kullanıcıya gösteriyoruz. Resmin görülebilmesi için url'li de ekleyelim. hastahane/urls.py dosyasına ekliyoruz.
url(r'profil/profil_resimleri/(.*)','yonetim.views.profil_resimleri'),
Artık linke tıklandığında resmin açılacağı bir sayfaya gidilecek. Son olarak kullanıcıya yönetim sayfasında kendi resmini gösterelim. Bunun için yonetim/sablonlar/yonetim_giris.html şablonuna şu satırı ekliyoruz.
<img src="/dosyalar/resimler/{{request.user.KullaniciProfili.resim.url}}" width="150" height="150"/>
Tarayıcıdan http://localhost:8000/yonetim/ yazdığınızda kullanıcı resmini de görebilirsiniz.
Kullanıcı profili oluşturmak bu kadar.
Kullanıcı profili oluşturabilmek için veri tabanında yeni bir tablo oluşturduk. Bu tabloyu yonetim sayfasına eklemediğimiz için Djando Yönetim Panelinde bu tabloyu göremeyeceksiniz. Görebilmek için yonetim/admin.py dosyasına şu satırı ekleyelim.
admin.site.register(KullaniciProfili)
Artık Yönetim Panelinden de kullanıcı profillerini yönetebiliriz. Tablonun daha anlaşılır olabilmesi için KullaniciProfili modeline şu satırları ekleyebilirsiniz.
    def __unicode__(self):
  return u'%s' % (self.user)
Yönetim Paneline Kullanıcı Profilleri alanı geldi.
Artık projemizdeki kullanıcılar profile sahip olmuş oldu.
Kaynakça

4 Temmuz 2014 Cuma

CAPTCHA Kullanımı

CAPTCHA kelimesini ilk defa duymuş olabilirsiniz. Ama hayatınız da en az bir kere kullanmışsınızdır. CAPTCHA kısaca; insan ile bilgisayarı birbirinden ayırmak için kullanılan bir projedir. Merak edeneler CAPTCHA nedir? tıklayarak okuyabilirler. Zaten hemen anlayacaksınız. Google'ın CAPTCHA'sı
Windows Üzerine Kurulum
CAPTCHA'yı kurabilmek için iki şeye ihtiyacımız vardır;
  • PIL(Python Imagin Library)
  • Python Setuptools
CAPTCHA'yı kurabilmemiz için sistemimize bu ikisini kurmamız gerekiyor. Bu işlemleri yaptıktan sonra artık CAPTCHA'yı kurabiliriz. Bunun için https://github.com/mbi/django-simple-captcha adresinden CAPTCHA'yı indirin. İndirdikten sonra zip dosyasından çıkartın. Komut satırında-cmd- çıkarttığınız dizin içerisine girerek şu komutu verin.
python.exe setup.py install
Bu komutu verdikten sonra CAPTCHA kurulmuş olacaktır.
Linux Üzerine Kurulum
Linux üzerine kurulumun Windows'tan hiç bir farkı yok aslında. Yine yapmamız gereken PIL ve Setuptools'u kurmak. Gerçi bunlar Linux'ta kurulu olarak geliyor. Ardından aynı şekilde verdiğimiz linkten CAPTCHA'yı indirerek aynı komutu vererek kurmalısınız. Biz sizin için bu işlemlerin komut satırından yapılan halini şu şekilde verelim.
apt-get install python-imaging #PIL
apt-get install python-setuptools
wget https://github.com/mbi/django-simple-captcha/zipball/master
unzip master
cd mbi-django-simple-captcha-060b00e
python setup.py install
CAPTCHA'yı kurduğumuzu göre artık projemize dahil edebiliriz. Bunun için hastahane/settings.py dosyasında INSTALLED_APPS değişkenine şu satırı eklemeliyiz.
'captcha',
Veritabanında CAPTCHA ile ilgili tabloları oluşturmak için komut satırından şu komutu çalıştıralım;
python manage.py syncdb
Creating tables ...
Creating table captcha_captchastore
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
Kurulum tamam mı? Bunu denetleyelim. Komut satırından şu komutu verin.
python manage.py test captcha
Creating test database for alias 'default'...
....................
----------------------------------------------------------------------
Ran 20 tests in 0.772s

OK
Destroying test database for alias 'default'...
Eğer sizde bu şekilde hata ile karşılaşmadıysanız kurulum tamam demektir. Kurulum tamam olduğuna göre artık kullanabiliriz.
hastahane/urls.py dosyasının en altına şu satırları ekleyin.
urlpatterns += patterns('',
                        url(r'^captcha/',include('captcha.urls')),
                        )
Artık sadece CAPTCHA'yı kullanmak istediğimiz formda kullanmak kaldı. Bir önceki başlıkta kullanıcı kayıt formu hazırlamıştık. Şimdi bu hazırladığımız forma CAPTCHA'yı ekleyelim. Bunu için ilk önce hastahane/forms.py dosyasına dahil etmemiz gerekiyor. Şu satırı ekleyin;
from captcha.fields import CaptchaField
Bu dosyada tanımlamış olduğumu KullaniciKayitFormu sınıfına dahil edebiliriz. Bunun için bu KullaniciKayitFormu sınıfının ilk satırlarına şu kodları ekliyoruz.
    captcha = CaptchaField() ##captchanın dahil edilmesi
    captcha.help_text='Resimde gördüğünüz harfleri kutuya yazınız.' #kullanıcıya gösterilecek açıklama
    captcha.label='Doğrulama Kodu' #Etiket. Eğer kullanılmazsa var sayılan olarak CAPTCHA'dır.
Bu satırları ekledikten sonra KullaniciKayitFormu'nun son hali şu şekilde olacaktır.
from captcha.fields import CaptchaField

class KullaniciKayitFormu(UserCreationForm):
    captcha = CaptchaField()
    captcha.help_text='Resimde gördüğünüz harfleri kutuya yazınız.'
    captcha.label='Doğrulama Kodu'
    class Meta:
        model = User
        fields = ('first_name','last_name','username','email')

    def clean_email(self):
        if not self.cleaned_data['email']:
            raise forms.ValidationError(u'E-posta adresi girin.')

        if User.objects.filter(email__iexact=self.cleaned_data['email']):
            raise forms.ValidationError(
                u'''
                Bu e-posta başka bir kullanıcı tarafından kullanılıyor.
                Başka bir e-posta kullanmayı deneyin.
                '''
            )
        return self.cleaned_data['email']
CAPTCHA'nın kullanımı bu kadar. Geri her şeyi Django bizim için halledecek. Kontrol kısımları ile biz ilgilenmiyoruz. Onlar Django'nun işi. Gerçekten basit değil mi?
Taryıcıdan http://localhost:8000/kayit/ yazarak kayıt formuna gidelim. Şöyle bir sayfa ile karşılaşacağız.
CAPTCHA ile ilgili ayarları hastahane/settings.py dosyası içersinde yapabiliriz. Varsayılan olarak dört karakterden oluşan bir CAPTCHA görüyorsunuz. Bunu değiştirmek isterseniz settings.py dosyasına şu satırı eklemeniz yeterli.
CAPTCHA_LENGTH = 6
Varsayılan olarak 22px boyutundadır. Değiştirmek için;
CAPTCHA_FONT_SIZE = 25
Arka plan rengi varsayılan olarak #ffffff -beyaz- gelmektedir. Değiştirmek için;
CAPTCHA_BACKGROUND_COLOR = '#ddd'
Bu kullanımda CAPTCHA karakterleri rastgele oluşturulmaktadır. Eğer istersek bir kelime listesinden de seçim yaptırabiliriz. Eğer bir Linux kullanıyorsanız ve yol belirtmez iseniz varsayılan olarak /usr/share/dict/words dosyası kullanılır.
CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.word_challenge'
Şuan herhangi bir dosya belirtmediğimiz için Linux işletim sisteminde /usr/share/dict/words dosyası kullanılarak CAPTCHA oluşturuldu. Eğer kontrol ederseniz üretilen her kelimenin bu dosya içerisinde olduğunu görebilirsiniz. Windows işletim sistemi kullananlar ise kendileri bir liste edinerek yolunu belirtmeliyiz.
CAPTCHA_WORDS_DICTIONARY = '/home/mazlum/captcha.txt'
Artık üretilecek CAPTCHA'lar bu dosya içerisinden seçilecektir. Eğer istersek bu şekilde kelime olan bir CAPTCHA yerine aritmatiksel işlemlerin sonucunu soran bir CAPTCHA'da oluşturabiliriz. Bunun için şu satırı ekleyin;
CAPTCHA_NOISE_FUNCTIONS=None # Resmin arkasindaki bozuklukgu kaldirir.
CAPTCHA_LETTER_ROTATION=None # Karakterlerin yamuk durmasini engeller.
CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'
CAPTCHA_NOISE_FUNCTIONS değişkeni CAPTCHA'nın arkaplanını belirler. Varsayılan olarak şu şekilde gelmektedir.
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_arcs','captcha.helpers.noise_dots',)
Eğer isterseniz bu demet içerisinden herhangi birini kaldırabilirsiniz. Veya None kullanarak arka planın temiz olmasını sağlayabilirsiniz.
CAPTCHA_LETTER_ROTATION değişkeni CAPTCHA üzerindeki karakterlerin eğikliklerini belirler. Varsayılan olarak şu şekilde gelmektedir;
CAPTCHA_LETTER_ROTATION =(-35,+35)
Buradaki değerleri değiştirerek karakterlerin eğiklikleri ile oynayabilir. Veya None kullanarak düz durmalarını sağlayabilirsiniz.
CAPTCHA ile ilgili daha fazla ayara https://django-simple-captcha.readthedocs.org/en/latest/advanced.html adresinden ulaşabilirsiniz.
Kaynakça

2 Temmuz 2014 Çarşamba

Kullanıcı Kaydı

Var olan bir kullanıcının adını değiştirdik. Peki ya kullanıcı kaydı nasıl yaptırırız? Bazı web sitelerinde, "Üye olmadan burayı göremezsiniz.","Bu işlemi yapabilmek için üye olmalınız." bu tarz ifadeler ile karşılaşabilirsiniz. Şuan ki sistemlerin çoğunda kullanıcı kaydı yapılmaktadır. Biz de bu işlemi kendi sistemimize yapacağız. Gerçi yapmış olduğumuz sistemde bu pek mantıklı değil. Ama yine de göstermek için bunu yapalım.
Hatırlarsanız kullanıcının giriş yapabileceği bir sayfamız vardı. Bu sayfaya http://localhost:8000/accounts/login/ yazarak ulaşıyorduk. Şimdi bu sayfaya kullanıcıların kayıt olabileceği bir bağlantı ekleyelim.
Bu bağlantıyı eklemek için yapmamız gereken tek şey, login.html şablonumuza şu satırı eklemek.
 <p>Henüz kayıtlı değilseniz <a href="/kayit/">buraya tıklayarak kayıt olun.</a></p>
Bu bağlantı henüz çalışmayacak tabi ki. Çünkü henüz sayfayı oluşturmadık. Bunun için ilk önce kayıt görümünü oluşturalım. views.py ekliyoruz.
from django.contrib.auth.forms import UserCreationForm

def kayit(request):
    if request.method == "POST":
        form = UserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/accounts/login/')
    else:
        form = UserCreationForm()
    return render_to_response('registration/register.html',
                              locals(),
                              context_instance = RequestContext(request))
Burada dikkat ettiyseniz ilk önce UserCreationForm içe aktardık. Bu formu kullanıyoruz. Eğer kullanıcı başarılı bir şekilde kayıt olduysa login sayfasına yönlendiriyoruz. Şimdi şablonu oluşturmalıyız. Bunun için daha önceden hastahane dizini içerisinde oluşturduğumuz registration dizinine şablonu oluşturuyoruz. hastahane/registration/register.html
<!DOCTYPE html>
<html>
<head>
    <title>OKUL: Kullanıcı Kayıt Formu</title>
   <link rel="stylesheet" type="text/css" href="/dosyalar/style.css" />
</head>
<body>

<h1>Kullanıcı Kaydı</h1>

    <form action="" method="post">
        {% csrf_token %}
        <table border="1">
            {{ form }}
        </table>
        <input type="submit" value="Kayıt Yap" />
    </form>

</body>
</html>
Son olarak urls.py dosyasına bağlantıyı eklemeliyiz.
url('^kayit/$','yonetim.views.kayit')
Artık her şey hazır. Giriş sayfasındaki bağlantıya tıkladığımızda aşağıdaki gibi bir sayfa ile karşılaşacağız.
Kullanıcı bu formu doldurduğunda kayıt olmuş olacaktır. Ardından giriş yapması için giriş sayfasına yönlendirilecektir.
Kendi Kayıt Formumuz
Tabi kullanıcılar kayıt olurken sadece kullanıcı adı ve şifre istenmez. Bu bilgilerin yanında genellikle isim,soyisim,e-posta gibi alanlarda istenir. Django'da kayıt formu varsayılan olarak bu şekilde sadece kullanıcı adı ve şifreden oluşmaktadır. Biz de bu yüzden Django'nun bize sağlamış olduğu formu kullanarak kendi formumuzu oluşturacağız. İlk önce formu oluşturmalıyız. Bunun için forms.py dosyasına şu kodları ekliyoruz.
yonetim/forms.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class KullaniciKayitFormu(UserCreationForm):
    class Meta:
        model = User
        fields = ('first_name','last_name','username','email')

    def clean_email(self):
        if not self.cleaned_data['email']:
            raise forms.ValidationError(u'E-posta adresi girin.')

        if User.objects.filter(email__iexact=self.cleaned_data['email']):
            raise forms.ValidationError(
                u'''
                Bu e-posta başka bir kullanıcı tarafından kullanılıyor.
                Başka bir e-posta kullanmayı deneyin.
                '''
            )
        return self.cleaned_data['email']
Kayıt formu oluştururken model olarak Django User nesnesini kullanıyoruz. Bu form User nesnesinin tüm alanlarına sahip olacaktır. Biz bunu istemediğimiz için fields değişkeni ile istediğimiz alanları belirttik. clean_email işlevini daha önce de kullanmıştık. Girilen mail adresinin kontrolünü bu işlev ile yapıyoruz. İlk önce kullanıcı e-posta girmiş mi onu kontrol ediyor, ardından ise daha önce bu e-posta ile kayıt yapılmış mı onu kontrol ediyoruz. Formu oluşturduğumuza göre görünümü de değiştirmeliyiz. Az önce oluşturduğumuz kayit görünümünü şu şekilde değiştiriyoruz.
yonetim/views.py

def kayit(request):
    if request.method == "POST":
        form = KullaniciKayitFormu(request.POST)
        if form.is_valid():
            form.save()
            kullanici = authenticate(username=form.cleaned_data['username'],
                                     password=form.cleaned_data['password1'])
            if kullanici.is_authenticated():
                login(request,kullanici)
            return HttpResponseRedirect("/yonetim/")
    else:
        form = KullaniciKayitFormu()
    return render_to_response('registration/register.html',locals(),context_instance=RequestContext(request))

Bu görünümde öncekinden farklı olarak kullanıcıya giriş yaptırılmış ve yönetim sayfasına yönlendirilmiştir.
Daha önceden hatırlarsanız yönetim sayfasını sadece yönetici grubundan olan kullanıcıların görebilmesini sağlamıştık. Bu yüzden oluşturduğumuz kullanıcı bu sayfayı göremeyecektir. Kullanıcının sayfayı görmesini istiyorsanız yonetim görünümünden önceki şu satırı kaldırmalısınız.
@user_passes_test(yonetici_kontrol)
Artık kayıt formu aşağıdaki gibi görünecektir.
Kaynakça:
  • Mustafa Başer Django Kitabı

29 Haziran 2014 Pazar

Python ile Arp Zehirlemesi( Poisoning ) Tespiti

Python ile arp zehirlenmesi nasıl tespit edilir? Arp zehirlenmesi nedir? Arp poising örneğin bir kafede oturdunuz ve kafenin ağına bağlandınız. Bu durumda saldırgan ağ ile sizin aranıza girerek kendisini size gateway olarak gösterebilir. Bu şekilde trafiğinizi kendi üstüne çekerek bilgilerinizi çalabilir. İşte burada gateway değişme durumunu yakalayan Python scripti yazdık.
import os
from Tkinter import *

## Uyarı veren fonksiyon
def Alert():
    root = Tk()
    root.title('Alert Gateway')
    w =500
    h = 100
    ws = root.winfo_screenwidth()
    hs = root.winfo_screenheight()
    x = (ws/2) - (500/2)
    y = (hs/2) - (100/2)
    root.geometry('%dx%d+%d+%d' % (w, h, x, y))
    Message(root, text="Your gateway has been changed.", background='red', width=300,
          fg='ivory', relief=GROOVE).pack(padx=100, pady=10)
    root.mainloop()

##Program çalıştırıldığında gateway alınır.
gateway = (os.popen("route -n | grep 'UG[ \t]' | awk '{print $2}'")).read()
while 1:
    ##Daha sonra sürekli alınarak kıyaslama yapılır. Eğer değiştirilmiş ise uyarı verilir.
    gateway2 = (os.popen("route -n | grep 'UG[ \t]' | awk '{print $2}'")).read()
    if gateway != gateway2:
        Alert()
        break

Şifre Sıfırlama

Kullandığımız her sistemde bir kullanıcı adı ve şifreye sahip oluruz. Bazen bu şifreyi unuttuğumuz zamanlar olabiliyor. Genel olarak da güvenli olması sebebi ile kullanılan mail adresine şifre sıfırlama parolası gönderilerek kullanıcılara ya yeni şifreleri otomatik olarak atanır yada yeni şifre oluşturabilecekleri bir link gönderilir. İşte şimdi bu işlemi Django'da yapacağız. Bu işlemleri Django otomatik olarak yapar. Bizim bazı tanımlamalar yapmamız gerekiyor hepsi bu. Kullanıcıya e-posta ile yeni şifre alabileceği bir link göndereceğiz.
E-Posta Ayarları
Bu linki gönderebilmek için bir e-posta sunucusuna ihtiyacımız olacak. Siz eğer isterseniz sisteminize bir MTA(Mail Transfer Agent) kurabilir ve mail gönderme işlemini onun üzerinden yapabilirsiniz. Biz google'ın bize sağlamış olduğu sunucuyu kullanacağız. Bunun için sadece bir gmail hesabımızın olması yeterli. İlk önce settings.py dosyasında e-posta ayarlarımızı yapacağız.
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'gmailhesabınız'
EMAIL_HOST_PASSWORD = 'gmailsifreniz'
DEFAULT_FROM_EMAIL = 'mailadresi'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

Bu kodları settings.py dosyasına ekliyoruz.
Değişken Açıklama
EMAIL_HOST Posta sunucusunun ip adresi veya domaini. Eğer localda çalışan bir sunucu kullanıyorsanız localhost yazabilirsiniz. Yada doğrudan sunucunun ip adresini de yazabilirsiniz.
EMAIL_HOST_USER SMTP onaylaması isteniyorsa kullanıcı adı. Burada gmaili kullandığımız için gmail kullanıcı adımızı yazıyoruz.
EMAIL_HOST_PASSWPRD SMTP onaylaması isteniyorsa kullanıcı şifresi. Burda gmaili kullandığımız için gmail kullanıcı şifremizi yazıyoruz.
DEFAULT_FROM_EMAIL Kimden gittiği belli olmayan postalar için bu adresten gönderilir.
EMAIL_PORT Posta sunucusunun dinlediği port numarası.
EMAIL_USE_TLS Eğer SMTP sunucusu TLS kullanıyorsa TRUE yapmamız gerekir. Varsayılan olarak FALSE gelir. Genel olarak 587. portu dinleyen bağlantılar için kullanılır.
Sadece deneme yapmak için Python bize deneysel bir e-posta sunucusu sağlayabilir. Bunun için komut satırında şu komutu çalıştırın.
python -m smtpd -n -c DebuggingServer localhost:255
Bu komutu çalıştırdıktan sonra settings.py dosyasında yazdıklarımız için sadece şunları yazmanız yeterlidir.
EMAIL_HOST = 'localhost'
EMAIL_PORT = 2525
E-Posta Gönderme
Ayarlarımız yaptığımıza göre artık posta gönderebiliriz. İlk e-postayı python kabuğu ile gönderelim.
>>> from django.core.mail import send_mail
>>> send_mail('Test','Bu e-posta test amacı ile gönderilmiştir.','mazlum.agar@gmail.com',['mazlum.agar@hotmail.com'])
1
Eğer sizde de geriye 1 değeri döndürüldü ise posta gönderilmiş demektir. Eğer 1 değeri dönmez ise postanın neden gönderilmediğine dair ayrıntılı bilgi verilecektir.
send_mail fonksiyonundaki ilk argüman posta konusu, ikincisi posta içeriği, üçüncüsü kimden gideceği, dördüncüsü ise liste şeklinde postanın kimlere gideceği.
Eğer HTML içerikli bir posta göndermek isterseniz şu yöntemi kullanabilirsiniz.
>>> from django.core.mail import EmailMultiAlternatives
>>> konu = 'Test'
>>> kimden = 'mazlum.agar@hotmail.com'
>>> kime = 'mazlum.agar@gmail.com'
>>> metin_icerik = 'Test\n Bu bir test mailidir'
>>> html_icerik = '<h3>Test<h3><p>Bu bir test mailidir.</p>'
>>> posta = EmailMultiAlternatives(konu,metin_icerik,kimden,[kime])
>>> posta.attach_alternative(html_icerik,"text/html")
>>> 
>>> posta.send()
1
Site Adı
Hatırlarsanız Yönetim Paneli Sites Bölümünü Aktifleştirme yazısında Yönetim Panelinde Sites bölümünün nasıl aktifleştirileceğinden bahsetmiştik. İşte bu bölümden sitemizin alan ismini değiştirebiliriz. Django tüm siteler için varsayılan olarak example.com vermektedir.
Buradan site ismini değiştirebiliriz. Tabi her zaman olduğu gibi bu işlemi kabuktan nasıl yapabileceğimizi de gösterelim.
>>> from django.contrib.sites.models import Site
>>> site = Site.objects.get(id=1)
>>> site.name = "localhost"
>>> site.domain = "localhost:8000"
>>> site.save()
Şuan yerel makinede(localhost) çalıştığımız için bu şekilde yaptık. Gerçek bir projede site ismini yazmamız gerekir. Bu işlemi neden yaptık? Biraz sonra şifre sıfırlama işleminde mail gönderirken kullanıcıya yeni şifre oluşturabileceği bir link göndereceğiz. Django o linkte buraya yazdığımız site ismini kullanıyor. Eğer burayı düzeltmez ise link example.com ile başlayacaktır.
Şifre Sıfırlama Linkleri
urls.py dosyasında urlpatternsurlpatterns değişkenine aşağıdaki url'leri ekleyelim.
url(r'^accounts/password/reset/$',
        'django.contrib.auth.views.password_reset',
        {'post_reset_redirect':'/accounts/password/reset/done/'},
        name='password_reset'),

    url(r'^accounts/password/reset/done/$',
        'django.contrib.auth.views.password_reset_done',
        name='password_reset_done'),

    url(r'^accounts/password/reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
        'django.contrib.auth.views.password_reset_confirm',
        {'post_reset_redirect':'/accounts/password/done/'},
        name='password_reset_confirm'),

    url(r'^accounts/password/done/$',
        'django.contrib.auth.views.password_reset_complete',
        name='password_reset_complete'),
Tıpkı şifre değiştirme işleminde olduğu gibi şifre sıfırlarken de Django'nun Yönetim Panelini kullanacağız. Birazdan kendi şablonlarımızı da oluşturmayı göreceğiz. Url'leri eklediğimize göre taryıcıdan http://localhost:8000/accounts/password/reset/ yazdığınızda şifre sıfırlama penceresi ile karşılacaksınız.
Not: Bu url'leri istediğiniz gibi değiştirebilirsiniz.
Şifre sıfırlama linki için bizden e-posta istiyor. Burada kullanıcı kayıt olurken yazdığı e-postayı yazmak zorundadır. Şimdi kayıt olurken yazdığımız e-posta'yı girelim ve şifre sıfırlama mailini alalım. Gelen mail şu şekildedir.
Gördüğünüz gibi yeni şifre girebileceğimiz bir link gönderilmiş. Şimdi o linke tıklayalım.
Artık yeni şifremizi girebiliriz. Yönetim Paneli şablonlarını kullanarak şifre sıfırlamak bu kadar basit.
Kendi Şifre Sıfırlama Şablonlarımız
Şimdi Yönetim Paneli şablonlarını değilde kendi şablonlarımızı kullanarak şifre sıfırlama işlemini yapalım. Şifre değiştirme işleminde yaptığımız işlemlerin aynısı burası içinde geçerli. Tıpki şifre değiştirme şablonları gibi hastahane/hastahane/sablonlar/registration dosyası içerisine şablonlarımız oluşturuyoruz.
hastahane/hastahane/sablonlar/registration/password_reset_form.html
<!DOCTYPE html>
<html>
<head>
    <title>Şifre Sıfırlama İsteği</title>
     <link rel="stylesheet" type="text/css" href="/static/base.css" />
</head>
<body>
<h3>Şifre Sıfırlama</h3>
Şifrenizi unuttuysanız, aşağıdaki kutuya e-posta adresinizi giriniz. Size şifre sıfırlamanız için posta göndereceğiz.

<form action="" method="post">
    {% csrf_token %}
    {{form.email.errors}}
    <p>E-posta adresi:
    {{ form.email }}
    <input type="submit" value="Şifre Sıfırla" />
    </p>

</form>
</body>
</html>
hastahane/hastahane/sablonlar/registration/password_reset_done.html
<!DOCTYPE html>
<html>
<head>
    <title>Şifre Sıfırlama Tamamlandı</title>
    <link rel="stylesheet" type="text/css" href="/static/base.css" />
</head>
<body>

<h3>Şifre sıfırlama işlemi tamamlandı</h3>
Lütfen posta kutunuzu kontrol edin ve talimatları yerine getirin. Eğer e-posta alamadıysanız, doğru e-posta girdiğinizden emin olun.
</body>
</html>
hastahane/hastahane/sablonlar/registration/password_reset_email.html
{% autoescape off %}

Merhaba {{user.first_name}}

{{site_name}} web sitesinde şifre sıfırlama isteğinde bulunduğunuz için bu maili aldınız.
Eğer böyle bir istekte bulunmadıysanız bu maili göz ardı ediniz.

Lütfen, aşağıdaki linke tıklayarak yeni şifrenizi oluşturunuz.

{% block reset_link %}
{{protocol}}://{{domain}}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
{% endblock %}

Teşekkürler
{% endautoescape %}
hastahane/hastahane/sablonlar/registration/password_reset_confirm.html
<!DOCTYPE html>
<html>
<head>
    <title>Şifre Sıfırlama</title>
    <link rel="stylesheet" type="text/css" href="/static/base.css" />
</head>
<body>

{% if validlink %}

<h3>Şifre Sıfırlama</h3>

<form action="" method="post">
{% csrf_token %}
    <table border="1">
        {{form}}
    </table>
    <input type="submit" value="Şifre Değiştir" />
</form>
{% else %}
<p>Girdiğiniz link geçersiz. Şifre değiştirme isteğinde tekrardan bulunabilirsiniz.</p>
{% endif %}

</body>
</html>
Şablonlarımızı oluşturduk. Az önce Yönetim Paneli şablonlarını kullanırken yaptığımız işlemler aynen geçerli. Tarayıcıdan http://localhost:8000/accounts/password/reset/ yazarak şifre sıfırlama sayfasına gidiyoruz. Aşağıda kendi şablonlarımızın resimlerini görebilirsiniz.





Kullanıcı şifresini başarılı bir şekilde sıfırladık.
Kaynakça