# dokumentasi Django 2.0.8

# Menulis aplikasi Django pertama anda, bagian 4

Tutorial ini mulai dimana Tutorial 3 ditinggalkan. Kami sedang melanjutkan aplikasi jejak pendapat Jaringan dan akan fokus pada bentuk pengolahan sederhana dan meringkas kode kami.

## Tulis formulir sederhana

Mari kita memperbaharui cetakan rincian jejak pendapat ("polls/detail.html") dari tutorial terakhir, sehingga cetakan mengandung sebuah unsur `<form>` HTML:

polls/templates/polls/detail.html

    <h1>{{ question.question_text }}</h1>

    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

    <form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
    {% endfor %}
    <input type="submit" value="Vote" />
    </form>

Laporan cepat :

- Cetakan diatas menampilkan tombol radio untuk setiap pilihan pertanyaan. `nilai` dari setiap tombol adalah ID pilihan pertanyaan dikaitkan. `name` dari setiap tombol radio adalah `"pilihan"`. itu berarti, ketika seseorang memilih satu dari tombol radio dan mengajukan formulir, itu akan mengirim data POST `choice=#` dimana # adalah ID dari pilihan terpilih. Ini adalah konsep dasar dari formulir HTML.
- Kami mengatur `tindakan` formulir ke `{% url 'polls:vote' question.id %}`, dan kami menyetel `method="post"`. Menggunakan method="post"` (sebagai lawan dari `method="get"`) adalah sangat penting, karena aksi dari pengajuan formulir ini akan merubah data sisi-peladem. Kapanpun anda membuat formulir yang merubah data sisi-peladen, gunakan `method="post"`. Tip ini bukan khusus pada Django; ini hanya bagus untuk praktik pengembangan Jaringan.
- `forloop.counter` mengindikasikan seberapa kali the `untuk` etiket telah melalui putarannya
- Sejak kami membuat formulir POST (yang dapat mempunyai efek merubah data), kami harus khawatir tentang Pemalsuan Permintaan Lintas Situs. Untungnya, anda tidak harus khawatir terlalu keras, karena Django datang dengan sistem sangat mudah-digunakan untuk melindungi terhadapnya. Pendeknya, semua formulir POST disasarkan pada URL internal harus menggunakan etiket cetakan `{% csrf_token %}`.

Sekarang, mari kita membuat tampilan Django yang menangani data diajukan dan melakukan sesuatu dengannya. Ingat, di Tutorial 3, kami membuat URLconf untuk aplikasi jejak pendapat yang menyertakan baris ini:

polls/urls.py

    path('<int:question_id>/vote/', views.vote, name='vote'),

Kami juga membuat penerapan tiruan dari fungsi `vote()`. mari kita membuat versi sebenarnya. Tambah berikut ke `polls/views.py`:

polls/views.py

    from django.http import HttpResponse, HttpResponseRedirect
    from django.shortcuts import get_object_or_404, render
    from django.urls import reverse

    from .models import Choice, Question
    # ...
    def vote(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        try:
            selected_choice = question.choice_set.get(pk=request.POST['choice'])
        except (KeyError, Choice.DoesNotExist):
            # Redisplay the question voting form.
            return render(request, 'polls/detail.html', {
                'question': question,
                'error_message': "You didn't select a choice.",
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            # Always return an HttpResponseRedirect after successfully dealing
            # with POST data. This prevents data from being posted twice if a
            # user hits the Back button.
            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

Kode ini menyertakan beberapa hal kami belum tangani di tutorial ini:

- `request.POST` adalah obyek seperti-kamus yang membiarkan anda akses data diajukan oleh nama kunci. Di kasus ini, `request.POST['choice']` mengembalikan ID dari pilihan terpilih, sebagai string. Nilai `request.POST` selalu string.

  Catat bahwa Django juga menyediakan `request.GET` untuk mengakses data GET di cara sama -- tetapi kami eksplisit menggunakan `request.POST` di kode kami, untuk memastikan bahwa data hanya diubah melalui panggilan POST.

- `request.POST['choice']` akan menaikkan `KeyError` jika `pilihan` tidak disediakan di data POST. Kode diatas memeriksa untuk `KeyError` dan menampilkan kembali formulir pertanyaan dengan pesan kesalahan jika `pilihan` tidak diberikan.

- Setelah menaikkan jumlah pilihan, kode mengembalikan sebuah `HttpResponseRedirect` daripada `HttpResponse` biasa. `HttpResponseRedirect` mengambil argumen tunggal: URL dimana pengguna akan dialihkan (lihat titik berikut untuk bagaimana membangun URL di kasus ini).

  Seperti komentar Phyton diatas tunjukkan, anda harus selalu mengembalikan sebuah `HttpResponseRedirect` setelah berhasil berurusan dengan data POST. Tip ini bukan khusus pada Djangol ini hanya bagus praktik pengembangan Jaringan.

- Kami sedang menggunakan fungsi `reverse()` di pembangun `HttpResponseRedirect` di contoh ini. Fungsi ini membantu mengindari memiliki URL kode keras di fungsi tampilan. Itu diberikan nama dari tampilan yang kami ingin melewatkan kendali ke dan bagian variabel dari corak URL yang menunjuk ke tampilan itu. Dalam kasus ini, menggunakan URLconf kami setel di Tutorial 3, panggilan `reverse()` ini akan mengembalikan deretan karakter seperti

      '/polls/3/results/'

  dimana `3` adalah nilai dari `question.id`. Ini URL dialihkan akan memanggil tampilan `'results'` untuk menampilkan halaman akhir.

Seperti disebutkan di Tutorial 3, `request` adalah sebuah obyek `HttpRequest`. Untuk lebih pada obyek `HttpRequest`, lihat permintaan dan tanggapan dokumentasi.

Setelah seseorang memilih di pertanyaan, tampilan `vote()` mengalihkan ke halaman hasil untuk pertanyaan. Mari kita tulis tampilan itu:

polls/views.py

    from django.shortcuts import get_object_or_404, render

    def results(request, question_id):
        question = get_object_or_404(Question, pk=question_id)
        return render(request, 'polls/results.html', {'question': question})

Ini hampir tepat sama seperti tampilan `detail()` dari Tutorial 3. Perbedaannya adalah hanya nama cetakan. Kami akan memperbaiki kelebihan ini kemudian.

Sekarang, buat cetakan `polls/result.html`:

polls/templates/polls/results.html

    <h1>{{ question.question_text }}</h1>

    <ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
    {% endfor %}
    </ul>

    <a href="{% url 'polls:detail' question.id %}">Vote again?</a>

Sekarang, pergi ke `/polls/1/` di perambah anda dan pilih di pertanyaan. Anda seharusnya melihat halaman hasil yang mendapatkan pembaharuan setiap waktu anda memilih. Jika anda mengajukan formulir tanpa memilih pilihan, anda akan melihat pesan kesalahan.

Catatan

Kode untuk tampilan `vote()` kami mempunyai masalah kecil. Dia pertama mendapatkan obyek `selected_choice` dari basistada, lalu menghitung nilai baru dari `votes`, dan lalu menyimpan kembali ke basisdata. Jika dua pengguna dari situs jaringan anda mencoba memilih tepatnya waktu sama, ini mungkin menjadi salah: nilai sama, mari kita katakan 42, akan ditarik untuk `votes`. Lalu, untuk kedua pengguna nilai baru dari 43 dihitung dan disimpan, tetapi 44 akan menjadi nilai diharapkan.

Ini disebut kondisi jarang. Jika anda tertarik, anda dapat membaca Avoiding race conditions using F() untuk mempelajari bagaimana anda dapat mengatasi masalah ini.

## Gunakan tampilan umum: Sedikit kode lebih baik

Tampilan `detail()` (dari Tutorial 3) dan `results()` adalah sangat sederhana -- dan, seperti disebutkan diatas, berlebihan. Tampilan `index()`, yang menampilkan daftar jejak pendapat, adalah mirip.

Tampilan ini mewakili kasus umum dari pengembangan Jaringan dasar: mendapatkan data dari basisdata menurut parameter yang dilewatkan di URL, memuat sebuah cetakan dan mengembalikan cetakan dibuat. Karena ini adalah sudah umum, Django menyediakan jalan pintas, dipanggil sistem "tampilan umum".

Tampilan umum meringkaskan corak umum ke titik dimana anda tidak butuh menulis kode Phyton untuk menulis sebuah aplikasi.

Mari kita merubah aplikasi jejak pendapat kami untuk menggunakan sistem tampilan umum, jadi kami dapat menghapus kumpulan kode sendiri. Kami akan hanya mengambil beberapa langkah untuk membuat pergantian. Kami akan:

1. Rubah URLconf.
2. Hapus beberapa lama, tampilan tidak dibutuhkan.
3. Memperkenalkan tampilan baru berbasis pada tampilan umum Django.

Membaca untuk rinci.

Kenapa kode dikocok?

Umumnya, ketika menulis aplikasi Django, anda akan menilai apakah tampilan umum cocok untuk masalah anda, dan anda akan menggunakan mereka dari mulai, daripada mengurai kembali kode anda setengah jalan. Tetapi tutorial ini sengaja fokus pada penulisan tampilan "cara sulit" sampai sekarang, untuk fokus pada konsep inti.

Anda harus mengetahui matematika dasar sebelum mulai menggunakan penghitung.

### Merubah URLconf

Pertama, buka URLconf `polls/urls.py` dan rubah nya seperti itu:

polls/urls.py

    from django.urls import path

    from . import views

    app_name = 'polls'
    urlpatterns = [
        path('', views.IndexView.as_view(), name='index'),
        path('<int:pk>/', views.DetailView.as_view(), name='detail'),
        path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
        path('<int:question_id>/vote/', views.vote, name='vote'),
    ]

Catata bahwa nama dari pola tercocok dalam jalur string dari pola kedua dan ketiga telah berubah dari ```` to ````.

### Merubah tampilan

Selanjutnya, kami akan memindahkan tampilan `index`, `detail`, dan `results` lama dan menggunakan tampilan umum Django. Untuk melakukannya, buka berkas `polls/views.py` dan rubah seperti itu:

polls/views.py

    from django.http import HttpResponseRedirect
    from django.shortcuts import get_object_or_404, render
    from django.urls import reverse
    from django.views import generic

    from .models import Choice, Question

    class IndexView(generic.ListView):
        template_name = 'polls/index.html'
        context_object_name = 'latest_question_list'

        def get_queryset(self):
            """Return the last five published questions."""
            return Question.objects.order_by('-pub_date')[:5]

    class DetailView(generic.DetailView):
        model = Question
        template_name = 'polls/detail.html'

    class ResultsView(generic.DetailView):
        model = Question
        template_name = 'polls/results.html'

    def vote(request, question_id):
        ... # same as above, no changes needed.

Kami sedang menggunaan dua tampilan umum disini: `ListView` dan `DetailView`. Masing-masing, kedua tampilan tersebut meringkaskan "menampilkan daftar obyek" dan "menampilan halaman rinci untuk jenis khusus dari obyek."

- Setiap tampilan umum butuh diketahui model apa akan bertindak. Ini disediakan menggunakan atribut `model`.
- Tampilan umum `DetailView` mengharapkan nilai kunci utama ditangkap dari URL untuk dipanggil `"pk"`, jadi kami telah merubah `question_id` menjadi `pk` untuk tampilan umum.

Secara awal, tampilan umum `DetailView` menggunakan cetakan dipanggil `<app name>/<model name>_detail.html`. Di kasus kami, dia akan menggunakan cetakan `"polls/question_detail.html"`. Atribut `template_name` digunakan untuk memberitahu Django untuk menggunakan cetakan spesifik dibandingkan dari nama cetakan awal otomatis dibangkitkan. Kami juga menentukan `template_name` untuk tampilan daftar `results` -- ini memastikan bahwa tampilan hasil dan tampilan rinci mempunyai penampilan ebrbeda ketika dibentuk, bahkan mereka berdua `DetailView` dibelakang layar.

Demikian pula, tampilan umum `ListView` menggunakan cetakan awal disebut `<app name>/<model name>_list.html`; kami menggunakan `template_name` untuk memberitahu `ListView` untuk menggunakan cetakan `"polls/index.html"` kami yang ada.

Di bagian sebelumnya dari tutorial, cetakan telah disediakan dengan konteks yang mengandung variabel konteks `question` dan `latest_question_list`. Untuk `DetailView` variabel `question` disediakan otomatis -- sejak kami menggunakan model Django (`Question`), Django dapat menentukan nama yang sesuai untuk variabel konteks. Bagaimanapun, untuk ListView, otomatis membangkitkan variabel konteks adalah `question_list`. Untuk menimpa ini kami menyediakan atribut `context_object_name`, menentukan bahwa kami ingin menggunakan `latest_question_list`. Sebagai pendekatan lain, anda dapat merubah cetakan anda untuk mencocokkan variabel konteks awal -- tetap akan lebih mudah untuk hanya mengatakan Django menggunakan variabel anda inginkan.

Jalankan peladen, dan gunakan aplikasi jejak pendapat baru anda berdasarkan tampilan umum.

Untuk rincian penuh pada tampilan umum, lihat dokumentasi tampilan umum.

Ketika anda nyaman dengan formulir dan tampilan umum, baca bagian 5 dari tutorial ini untuk mempelajari tentang aplikasi jejak pendapat kami.