{Django Öğreniyorum}
Günümüz kusursuzcuları için ağ çatısı

İlk Django Uygulamanızı Yazma, Bölüm 3

Bu sayfa Öğretici 2‘nin kaldığı yerden devam ediyor. Ağ siteleri uygulamasına devam ediyoruz. Ortak bir arayüz oluşturmaya odaklanacağız. Konumuz “görünümler” (views).

Önbakış

Görünüm, Django uygulamanızda genellikle belirli bir işleve hizmet eden ve belirli bir şablona sahip olan ağ sayfası türüdür. Örneğin, bir blog uygulamasında aşağıdaki görüntülemelere sahip olabilirsiniz:

Anket uygulamamızda aşağıdaki 4 görünüme sahip olacağız

Django’da ağ sayfaları ve diğer içerikler görüntüleme yöntemiyle sunulmuştur. Her görünüm basit bir Python işleviyle (veya sınıf tabanlı görünümler durumunda yöntemle) temsil edilir. Django istenen URL’yi inceleyerek (kesin olmak gerekirse URL’nin alan adının ardındaki kısmını) bir görünüm seçecektir.

Artık ağdaki zamanınızda ‘ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B’ gibi güzelliklere rastlamış olabilirsiniz. Django’nun bize göre çok daha zarif URL kalıpları sağladığını bilmekten mutluluk duyacaksınız.

Bir URL kalıbı, basitçe bir URL’nin genel biçimidir. Örneğin: /haberarsivi/< yil >/< ay >

Bir URL’den bir görünüm elde etmek için Django, ‘URLconf’ olarak bilinenleri kullanır. Bir URLconf, URL kalıplarını görünümlere eşler

Bu eğitimde URLconf’ların kullanımı konusunda temel yönergeler verilmektedir ve daha fazla bilgi için URL yönlendiricisine başvurabilirsiniz.


Görünümler Yazıyoruz

Şimdi anketler/views.py için birkaç görünüm daha ekleyelim. Bu görünümler biraz farklıdır çünkü bir öğe almaktadırlar:

anketler/views.py


    def ayrinti(request, soru_id):
        return HttpResponse("%s kimlikli soruya bakıyorsun." % soru_id)

    def sonuclar(request, soru_id):
        response = "%s kimlikli sorunun sonuçlarına bakıyorsun."
        return HttpResponse(response % soru_id)

    def oy(request, soru_id):
        return HttpResponse("%s kimlikli soruya oy veriyorsun." % soru_id)
  

Aşağıdaki yeni path() çağrılarını ekleyerek bu yeni görünümleri anketler.urls modülüne bağlayın:

anketler/urls.py


    from django.urls import path

    from . import views

    urlpatterns = [
        # örnek: /anketler/
        path('', views.index, name='index'),
        # örnek: /anketler/5/
        path('<int:soru_id>/', views.ayrinti, name='ayrinti'),
        # örnek: /anketler/5/sonuclar/
        path('<int:soru_id>/sonuclar/', views.sonuclar, name='sonuclar'),
        # örnek: /anketler/5/oy/
        path('<int:soru_id>/oy/', views.oy, name='oy'),
    ]
  

Tarayıcınızda “/anketler/34/” adresinden bir göz atın. ayrinti() yöntemini çalıştırır ve URL’de sağladığınız kimliği görüntüler. “/anketler/34/sonuclar/” ve “/anketler/34/oy/” adreslerini de deneyin. Bunlar yer tutucu sonuçlarını ve oylama sayfalarını görüntüler.

Birisi ağ sitenizden bir sayfa istediğinde “/anketler/34/” deneyin. Django benimsite.urls Python modülünü yükleyecektir. Çünkü ROOT_URLCONF ayarı öyle tanımlanmıştır. urlparrents adlı değişkeni bulur ve desenleri sırasıyla işleme geçirir. “anketler/” eşleşmesini bulduktan sonra bir sonraki “34/” değiştirgesini eşleştirir. Sonraki işleme için ‘anketler.urls’ URLconf’a gönderir. Orada ‘<int: soru_id>/’ ile eşleşir ve ayrıntı() görünümüne şöyle bir çağrı gelir:


    ayrinti(request=<HttpRequest object>, soru_id=34)
  

soru_id = 34 bölümü <int:soru_id>‘den gelir. Köşeli parantezler kullanılarak URL’nin bir kısmı “yakalanır” ve onu görünüm işlevine anahtar sözcük ögesi olarak gönderir. :soru_id> dizesinin bir kısmı, eşleşen desen tanımlamak için kullanılacak adı ve <int: bölümü, URL yolunun bu bölümüyle hangi kalıpların eşleşmesi gerektiğini belirleyen dönüştürücüdür

.html gibi URL cruft eklemenize gerek yoktur. İstemediğiniz durumda aşağıdakileri yapabilirsiniz.


    path('anketler/enyeniler.html', views.index),
  

fakat böyle bir şey yapmak gereksizdir.


Gerçekten bir şeyler yapan görünümler yazmak

Her görünüm iki şeyden birini yapmaktan sorumludur: istenen sayfanın içeriğini içeren bir HttpResponse nesenesinin döndürülmesi veya Http404 gibi bir istisna yükseltilmesi. Gerisi size kalmış.

Görünüm bir veritabanındaki kayıtları okuyabilir veya okyuamaz. Django’nun veya üçüncü parti bir Python şablon örgüsü gibi şablon örgüsü kullanabilir veya kullanamaz. İstediğiniz Python kütüphanelerini kullanarak, bir PDF dosyası, çıktı XML, anında bir ZIP dosyası oluşturma, istediğini herhangi bir şey oluşturabilir.

Django’nun istediği HttpResponse olmasıdır. Ya da bir istisna.

Çünkü kullanışlıdır, Django’nun Öğretici 2‘de ele aldığı kendi veritabanı API’sini kullanalım. İşte yeni bir index() görünümde, yayın tarihine göre, sistemdeki en son 5 anket sorusunu virgüllerle ayırarak gösteren bir tutsak var:

anketler/views.py


from django.http import HttpResponse

from .models import Soru


def index(request):
    son_sorular_listesi = Soru.objects.order_by('-yayim_tarihi')[:5]
    cikti = ', '.join([q.soru_metni for q in son_sorular_listesi])
    return HttpResponse(cikti)

# Kalan görünüşleri (ayrinti, sonuclar, oy) olduğu gibi bırakın.

Yine de burada bir sorun var: sayfanın tasarımı görünümde kodlanmış. Sayfanın görünümü değiştirmek isterseniz bu Python kodunu düzenlemeni gerekir. Django2nun şablon örgüsünü, görünüm kullanabileceği bir şablon oluşturarak Python’dan ayırmak için kullanalım.

İlk olarak, anketler dizininde “templates” adı verilen bir dizin oluşturun. Django şablonları arar.

Projenizin TEMPLATES ayarı, Django’nun şablonları nasıl yükleyeceğini ve işleyeceğini açıklıyor. Varsayılan ayarlar dosyası, APP_DIRS seçeneği True olarak ayarlanmış bir DjangoTemplates arka ucunu yapılandırır. Sözleşmeye göre, DjangoTemplates, INSTALLED_APPS’in her birinde bir “şablon” alt dizini arar.

Yeni olşturduğunuz şablon (templates) dizininde, anketler adlı başka bir dizin oluşturun ve bunun içinde index.html adlı bir dosya oluşturun. Başka bir değişle, şablonunuz anketler/templates/anketler/index.html olmalıdır. app_directoreis şablon yükleyicisinin yukarıda açıklanan şekilde çalışması nedeniyle Django içindeki bu şablona anketler/index.html gibi bakabilirsiniz.

Şablon ad alanları

Şimdi şablonlarımızı doğrudan anketler/templates dizinine koyarak ilerleyebiliriz (Başka bir anket alt dizini oluşturmak yerine), ama aslına bakarsak kötü bir fikir olurdu. Django, bulduğu ilk şablonu seçecek ve adı aynı olan bir şablonun farklı bir uygulamada bulunması durumunda aralarında ayrım yapamayan Django olurdu. Django’yu doğru olana yönlendirebilmemiz lazım ve bunları sağlamak için en kolay yol onları isimlendirmektir. Yani, bu şablonları uygulamanın kendisi için adlandırılan başka bir dizine yerleştirerek…

Şablona aşağıdaki kodu koyun:

anketler/templates/anketler/index.html


    {% if son_sorular_listesi %}
        <ul>
        {% for soru in son_sorular_listesi %}
            <li><a href="/anketler/{{ soru.id }}/">{{ soru.soru_metni }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>Anket bulunamadı.</p>
    {% endif %}
  

Şablonu kullanmak için dizin görünümünü anketler/views.py dosyasında güncelleyelim.

anketler/views.py


  from django.http import HttpResponse
  from django.template import loader

  from .models import Soru


  def index(request):
      son_sorular_listesi = Soru.objects.order_by('-yayim_tarihi')[:5]
      template = loader.get_template('anketler/index.html')
      cikti = {
          'son_sorular_listesi': son_sorular_listesi,
      }
      return HttpResponse(template.render(cikti, request))

Bu kod, anketler/index.html adlı şablonu yükler ve bir bağlamı iletir. Bağlam, Python nesnelerine bir sözlük eşleme şablon adlarıdır.

Tarayıcınısı “/anketler/” adresine getirerek sayfayı yükleyin ve Öğretici 2‘nin “Yenilikler ne?” sorusunu içeren madde işaretli bir liste görmelisiniz. Bağlantı, sorununun ayrıntı sayfasına işaret eder.

Kısayol: render()

Bir şablon yüklemek, bir bağlamı doldurmak ve işlenmiş şablonun sonucuyla bir HttpResponse nesnesi döndürmek çok yaygın bir deyimdir. Django bir kısayol sağlar. İşte tam index() görünümü, yeniden yazıldı:

anketler/views.py


  from django.shortcuts import render

  from .models import Soru


  def index(request):
      son_sorular_listesi = Soru.objects.order_by('-yayim_tarihi')[:5]
      cikti = {'son_sorular_listesi': son_sorular_listesi}
      return render(request, 'anketler/index.html', cikti)

Bunu tüm bu görünümlerde yaptıktan sonra yükleyici ve HttpRepsonse’u artık içe aktarmamız gerekmediğini unutmayın (ayrıntılar, sonuçlar ve oy için saplama yöntemleri hala varsa HttpRepsonse’u saklamak istersiniz).

render() işlevi, istek nesenesini ilk bağımsız değişkeni, bir şablon adını ikinci bağımsız değişkeni ve bir sözlüğü isteğe bağlı üçüncü bağımsız değişkeni alır. Verilen bağlamla oluşturulmuş verilen şablonun bir HttpResponse nesnesi döndürür.


404 hatası yükseltme

Şimdi, soru ayrıntısı görünümüyle uğraşalım. Belirli bir anket için soru metnini görüntüleyen sayfa. İşte görünüm:

anketler/views.py


  from django.http import Http404
  from django.shortcuts import render

  from .models import Soru
  # ...
  def ayrinti(request, soru_id):
      try:
          soru = Soru.objects.get(pk=soru_id)
      except Soru.DoesNotExist:
          raise Http404("Soru bulunamadı")
      return render(request, 'anketler/ayrinti.html', {'soru': soru})

Buradaki yeni kavram: İstek, istenen kimliği olan bir soru bulunmaması halinde Http404 istisnasını yükseltir.

anketler/ayrinti.html şablonuna neyin koyabileceğini tartışacağız, ancak yukarıdaki örneği çabucak kullanmak istiyorsanız, sadece aşağırdakileri içeren bir dosya oluşturun:

anketler/templates/anketler/ayrinti.html


{{ question }}

Şimdi başlayacaksın

Kısayol: get_object_or_404()

get() işlevini kullanmak ve nesne yoksa Http404’ü yükseltmek çok yaygın bir deyimdir. Djano buna bir kısayol sağlar. ayrinti() görünümü aşağıda yeniden yazılmıştır:

anketler/views.py


from django.shortcuts import get_object_or_404, render

from .models import Soru
# ...
def ayrinti(request, soru_id):
    soru = get_object_or_404(Soru, pk=soru_id)
    return render(request, 'anketler/ayrinti.html', {'soru': soru})

get_object_or_404() işlevi, ilk öğe olarak bir Django modeli ve model yöneticisinin get() işlevine ilettiği rasgele sayıda anahtar sözcük bağımsız değişkeni alır. Nesne yoksa Http404’ü yükseltir.

Felsefesi

Neden daha yüksek bir seviyede ObjectDoesNotExist istisnalarını doğal olarak yakalamak yerine bir yardımcı işlev get_object_or_404() kullanılıyor veya model API’si ObjectDoesNotExist yerine Http404 yükseltiliyor?

Çünkü model katmanı görüntüleme katıyla eşleştirir. Django’nun en önemli tasarım amaçlarından biri, gevşek bağlamayı korumaktır. Bazı kontrollü bağlantılar django.shortcuts modülünde tanıtıldı.

get_object_or_404() gibi çalışan, ancak get() yerine filter() işlevini kullanan bir get_list_or_404() işlevi de vardır. Liste boşsa, Http404’ü yükseltir.


Şablon sistemini kullan

Anket uygulaması için ayrinti() görünümüne geri dönün. Bağlam değişkeni sorusu göz önüne alındığında, anketler/ayrinti.html şablonunun nasıl görünebileceğini burada görebilirsiniz:

anketler/templates/anketler/ayrinti.html


  <h1>{{ soru.soru_metni }}</h1>
  <ul>
  {% for secim in soru.secim_set.all %}
      <li>{{ secim.secim_metni }}</li>
  {% endfor %}
  </ul>

Şablon örgüsü, değişken niteliklere erişmek için nokta-gözatma sözdizimini kullanır. {{soru.soru_metni}} örneğinde, ilk Django nesne sorusunda sözlükte arama yapar. Başarısız olursa, bir öznitelik araştırması dener ki bu da işe yarar. Öznitelik araması başarısız olursa, bir liste dizini araştırması denemiş olurdu.

Yöntem çağrısı, {% for %} döngüsünde olur: soru.secim_set.all, Secim nesnelerinin yinelenebilirliğini döndüren ve {% for %} etiketinde kullanılacak kodu soru.secim_set.all() olarak yorumlanır.

Şablonlar hakkında daha fazla bilgi için şablon rehberine bakın.


Şablonlarda kodlanmış URL’leri kaldırma

anketler/index.html şablonundaki bir sorunun bağlantısını yazdığımızda hattın kusmen sabit kod olduğunu untumayın:


<li><a href="/anketler/{{ soru.id }}/">{{ soru.soru_metni }}</a></li>

Bu kodlanmış, sıkı bağlanmış yaklaşımla ilgili sorun, birçok şablonla projelerde URL’leri değiştirmek zor oluyor. Bununla birlikte, anketler.urls modülünde path() işlevinde name öğesi tanımladığınızdan, {% for %} şablon etiketini kullanarak yapılandırmalarınızda tanımlanan belirli URL yollarına güvenebilirsiniz:


<li><a href="{% url 'ayrinti' soru.id %}">{{ soru.soru_metni }}</a></li>

Bunun çalışması, anketler.urls modülünde belirtilen URL tanımına bakmaktır. ‘ayrinti’ için URL adının tam olarak nerede tanımlandığını görebilirsiniz.


  ...
  # 'name' değeri {% url %} şablon etiketi tarafından çağrıldığı gibi
  path('<int:soru_id>/', views.ayrinti, name='ayrinti'),
  ...

Anketlerin ayrıntı görünümünün URL’sini şablonda (veya şablonlarda) yapmak yerine, anketler/ozellikler/12/ gibi bir şeye değiştirmek isterseniz, bunu anketler/urls.py dosyasında değiştireceksiniz:


  ...
  # 'name' değeri {% url %} şablon etiketi tarafından çağrıldığı gibi
  path('ozellikler/<int:soru_id>/', views.ayrinti, name='ayrinti'),
  ...

URL isimleri isimlendirme

Öğretici projede sadece bir uygulama var, anketler. Gerçek Django projelerinde beş, on, yirmi veya daha fazla uygulama olabilir. Django, aralarında bulunan URL adlarını nasıl ayırt eder? Örneğin, anketler uygulaması bir “ayrinti” görünümüne sahiptir ve bu nedenle bir blog için olan aynı projedeki bir uygulama olabilir. Django {% url %} şablon etkitekini kullanırken bir url için hangi uygulama görüntülemesinin oluşturulacağını bilmesi için bunu nasıl yapar?

Cevap. URLconf’unuzda ad alanları eklemektir. anketler/urls.py dosyasında, devam edin ve uygulama ad alanını ayarlamak için bir app_name ekleyin:

anketler/urls.py


  from django.urls import path

  from . import views

  app_name = 'anketler'
  urlpatterns = [
      path('', views.index, name='index'),
      path('<int:soru_id>/', views.ayrinti, name='ayrinti'),
      path('<int:soru_id>/sonuclar/', views.sonuclar, name='sonuclar'),
      path('<int:soru_id>/oy/', views.oy, name='oy'),
  ]

anketler/index.html şablonunuzu şu adresten değiştirin:

anketler/templates/anketler/index.html


<li><a href="{% url 'ayrinti' soru.id %}">{{ soru.soru_metni }}</a></li>

ad alanı ‘ayrinti’ görünümünü işaret etmek için:


<li><a href="{% url 'anketler:ayrinti' soru.id %}">{{ soru.soru_metni }}</a></li>

Görünümler yazarken rahat edersiniz, basit biçim işleme ve genel görünümler hakkında bilgi edinmek için öğretici 4 gölümüne geçin.