Bu sayfa Öğretici 3‘ün kaldığı yerden devam ediyor. Anket uygulamasına devam ediyoruz ve basit biçim işleme ve kodumuzu kesme üzerine odaklanacağız.
Şablonun bir HTML <form> öğesi içerdiği şekilde, son öğreticide geldiğimiz aşamadaki anket ayrıntısı şablonunu güncelleyelim. (‘anketler/ayrinti.html’):
anketler/templates/anketler/ayrinti.html
<h1>{{ soru.soru_metni }}</h1>
{% if hata_mesaji %}<p><strong>{{ hata_mesaji }}</strong></p>{% endif %}
<form action="{% url 'anketler:oy' soru.id %}" method="post">
{% csrf_token %}
{% for secim in soru.secim_set.all %}
<input type="radio" name="secim" id="secim{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="secim{{ forloop.counter }}">{{ secim.secim_metni }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
Hızlı bir özet:
Şimdi, gönderilen verileri işleyen ve onunla birlikte bir şey yapan bir Django görünümü oluşturalım. Öğretici 3‘te, bu satırı içeren yoklama uygulamaları için bir URLconf oluşturduk:
anketler/urls.py
path('<int:soru_id>/oy/', views.oy, name='oy'),
Ayrıca, oy() işlevinin kukla bir uygulamasını oluşturduk. Hay de, gerçek bir sürüm oluşturalım. anketler/views.py dosyasına aşağıdakileri ekleyin:
anketler/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from .models import Secim, Soru
# ...
def oy(request, soru_id):
soru = get_object_or_404(Soru, pk=soru_id)
try:
secim_secildi = soru.secim_set.get(pk=request.POST['secim'])
except (KeyError, Secim.DoesNotExist):
# Soruyu oylama biçimini yeniden görüntüleme.
return render(request, 'anketler/ayrinti.html', {
'soru': soru,
'hata_mesaji': "Herhangi bir seçim yapmadınız.",
})
else:
secim_secildi.oylar += 1
secim_secildi.save()
# POST verileri her zmaan bir HttpResponseRedirect döndürür. Bu, bir kullanıcı "geri" düğmesine basarsa verilerin iki kez gönderilmesini önler.
return HttpResponseRedirect(reverse('anketler:sonuclar', args=(soru.id,)))
Bu kod, bu derste henüz ele almadığımız birkaç şeyi içermektedir:
request.POST, anahtar adına göre gönderilen verilere erişmenizi sağlayan sözdizimi benzeri bir nesnedir. Bu durumda request.POST[‘secim’], seçilen tercihin kimliğini bir dize olarak döndürür. request.POST değerleri her zaman dizelerdir.
Django’nun aynı şekilde GET verisine erişmek için request.GET de sağladığını unutmayın. Ancak verilerimizin yalnızca bir POST çağrısı aracılığıyla değiştirilmesini sağlamak için request.POST kodumuzda açıkça kullanıyoruz.
request.POST[‘secim’] POST verilerinde seçim yapılmadığı takdirde KeyError değerini yükseltecektir. Yukarıdaki kod KeyError’u denetler ve seçim yapılmazsa soru biçimini bir hata mesajıyla tekrar görüntüler.
Seçim sayısını arttırdıktan sonra kod, normal bir HttpResponse yerine bir HttpResponseRedirect döndürür. HttpResponseRedirect, tek bir bağımsız değişkeni alır: kullanıcının yönlendirileceği URL (bu durumda URL’yi nasıl oluşturduğumuz konusunda aşağıdaki noktaya bakın).
Yukarıdaki Python yorumunda dikkat çekildiği gibi, her zaman bir POST verisi ile uğraştıktan sonra HttpResponseRedirect döndürmelidir. Bu ipucu, Django’ya özgü değildir; sadece iyi bir ağ geliştirme adetidir.
Bu örnekte HttpResponseRedirect yapıcısında reverse() işlevini kullanıyoruz. Bu işlev, görünüm işlevinde bir URL’nin sabit kodlanması gerekmeden yardımcı olur. Kontrolden geçirmek istediğimiz görünümü ve bu görünümü imleyen URL kalıbının değişken bölümü verilir. Bu durumda, Öğretici 3‘de kurduğumuz URLconf kullanılarak reverse() çağrısı aşağıdaki gibi bir dize döndürür:
'/anketler/3/sonuclar/'
burada soru.id’nin değeri 3’tür. Bu yönlendirilen URL daha sonra, sonuç sayfasını görüntülemek için ‘sonuclar’ görünümünü çağırır.
Öğretici 3‘de belirtildiği gibi, request bir HttpRequest nesnesidir. HttpRequest nesneleri hakkında daha fazla bilgi için istek ve yanıt (request ve response) belgelerine bakın.
Birisi bir soruyu oylamaya başladıktan sonra, oylama() görünümü sorunun sonuçlar sayfasına yönlendirilir. Bu görünümü yazalım:
anketler/views.py
from django.shortcuts import get_object_or_404, render
def sonuclar(request, question_id):
soru = get_object_or_404(Soru, pk=question_id)
return render(request, 'anketler/sonuclar.html', {'soru': soru})
Bu, Öğretici 3‘deki ayrinti() görünümüyle hemen hemen aynıdır. Tek fark şablon adıdır. Bu fazlalığı sonra çözeceğiz.
Şimdi bir anketler/sonuclar.html şablonu oluşturun:
anketler/templates/anketler/sonuclar.html
<h1>{{ soru.soru_metni }}</h1>
<ul>
{% for secim in soru.secim_set.all %}
<li>{{ secim.secim_metni }} -- {{ secim.oylar }} oy{{ secim.oylar|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'oylar:ayrinti' soru.id %}">Tekrar oyla?</a>
Şimdi, tarayıcınızda “/anketler/1/” adresine gidin ve soruyu oylayın. Her oy verdiğinizde güncellenen bir sonuç sayfası görmelisiniz. Bir seçeneği seçmeden biçimi gönderirseniz, hata iletisini görmelisiniz.
oy() görüntüsü kodumuz küçük bir sorun yaşıyor. Önce secim_secildi nesnesini veritabanından alır. Sonra oyların yeni değerini hesaplar ve daha sonra veritabanına geri kaydeder. Ağ sitenizin iki kullanıcısı aynı anda oy kullanmaya kalkarsa, bu yanlış olabilir: Aynı değeri her iki oy içinde alır. Ardında her iki kullanıcı için 43’ün yeni değeri hesaplanır ve kaydedilir. Ancak beklenen değerin sonucu 44 olur.
Buna yarış durumu denir. eğer ilgileniyorsanız bu sorunu nasıl çözebileceğinizi öğrenmek için yarış şartlarından kaçma F() konusunu okuyabilirsiniz.
ayrinti() (Öğretici 3‘den) ve sonuclar() görünümleri çok basittir. Yukarıda belirtildiği gibi gereğinden fazla… Anketlerin bir listesini görüntüleyen index() görünümüne benzer.
Bu görünümler, temel ağ geliştirmesinin ortak bir örneğini temsil eder: URL’den geçirilen bir değiştirgeye göre veritabanından veri alma, bir şablon yükleme ve işlenmiş şablonu iade etme. Bu çok yaygın olduğu için Django ‘genel görünümler’ örgüsü olarak adlandırılan bir kısayol sağlar.
Genel görünümler, yaygın kalıpları bir uygulama yazmak için Python kodunu yazmak zorunda kalmadığınız noktada soyutlar.
Anket uygulamamızı genel görünüm örgüsünü kullanacak şekilde dönüştürelim. Böylece kendi kodumuzdan bir demek silebiliriz. Dönüşümü yapmak için birkaç adım atmamız gerekecek. Yapacağımız şeyler:
Ayrıntılar için okumaya devam edin.
Genel olarak, bir Django uygulaması yazarken, jenerik görünümlerin sorununuza iyi uyum sağlayıp sağlamadığını değerlendireceksiniz ve kodunuzu ara ara yeniden biçimlendirmek yerine baştan kullanacaksınız. Ancak bu öğretici kısıtlı olarak, çekirdek kavramlara odaklanmak için şu ana kadar “uzun yoldan” görünümleri anlattı.
Bir hesap makinesini kullanmaya başlamadan önce temel matematiği öğrenmelisin meselesi…
İlk olarak, anketler/urls.py URLconf’i açın ve şu şekilde değiştirin:
anketler/urls.py
from django.urls import path
from . import views
app_name = 'anketler'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.AyrintiView.as_view(), name='ayrinti'),
path('<int:pk>/sonuclar/', views.SonuclarView.as_view(), name='sonuclar'),
path('<int:soru_id>/oy/', views.oy, name='oy'),
]
İkinci ve üçüncü desenlerin yol dizelerinde eşleşen desenin adı soru_id’den pk’ya geçtine dikkat edin.
Sırada… Eski dizin, ayrıntı ve sonuç görünümlerimizi kaldıracağız. Bunun yerine Django’nun genel görünümlerini kullanacağız. Bunu yapmak için anketler/views.py dosyasını açın ve şu şekilde değiştirin:
anketler/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views import generic
from .models import Secim, Soru
class IndexView(generic.ListView):
tema_adi = 'anketler/index.html'
baglam_nesnesinin_adi = 'son_sorular_listesi'
def get_queryset(self):
"""En son yayınlanan 5 soruyu getir."""
return Soru.objects.order_by('-yayim_tarihi')[:5]
class AyrintiView(generic.DetailView):
model = Soru
tema_adi = 'anketler/ayrinti.html'
class SonuclarView(generic.DetailView):
model = Soru
tema_adi = 'anketler/sonuclar.html'
def oy(request, soru_id):
... # bundan sonra değişiklik gerekmiyor.
Burada iki genel görünüm kullanıyoruz: ListeView ve AyrintiView. Her iki görünümde de “nesnelerin listesi gösteriliyor”. Belirli bir nesne türü için ayrıntı sayfası görüntüleme kavramlarını özetlemek gerekir.
Varsayılan olarak, AyrintiView genel görünümü /
Benzer şekide, ListeView genel görünümü /
Öğreticinin önceki bölümlerinde şablonlara soru ve son_sorular_listesi bağlam değişkenlerini içeren bir bağlam verilmiştir. AyrintiView için soru değişkeni doğal olarak sağlanır. Çünkü Django modeli (Soru) kullanıyoruz. Django bağlam değişkeni için uygun bir ad belirleyebiliyor. Bununla birlikte, ListeView iin doğal olarak oluşturulan bağlam değişkeni soru_listesi’dir. Bunu geçersiz kulmak için bunun yerine son_sorular_listesi’i kullanmak istediğimizi belirten baglam_nesnesinin_adi niteliğini sağlıyoruz. Seçeneksel yaklaşım olarak şablonlarınızı yeni varsayılan içerik değişkenleriyle eşleşecek şekilde değiştirebilirsiniz. Ancak Django’ya istediğiniz değişkeni kullanmasını söylemek çok daha kolaydır.
Sunucuyu çalıştırın ve genel görünümlere dayalı yeni anket uygulamanızı kullanın.
Genel görünümler hakkında ayrıntılı bilgi için genel görünüm belgelerine bakın.
Biçimlere ve genel görünümlere uygun olduğunda, anket uygulamalarımızı denemek hakkında bilgi edinebilirsiniz. Bunun için Öğretici 5 bölümünü okuyun.