---
title: "Menulis aplikasi Django pertama anda, bagian 3"
version: 3.2
locale: id
source: https://docs.djangoproject.com/id/3.2/intro/tutorial03/
canonical: https://djangodocs.dev/id/3.2/intro/tutorial03/
---
# Menulis aplikasi Django pertama anda, bagian 3

Tutorial ini mulai dimana [Tutorial 2](/id/3.2/intro/tutorial02/) tinggalkan. Kami sedang melanjutkan aplikasi jejak pendapat jaringan dan akan fokus membuat antarmuka umum -- "tampilan".

> **Dimana mendapatkan bantuan:**
>
> Jika anda memiliki masalah melalui tutorial ini, harap geser ke [Getting Help](/id/3.2/faq/help/) bagian dari FAQ.

## Ikhtisar

Sebuah tampilan adalah "jenis" dari halaman Jaringan di aplikasi Django yang umumnya melayani fungsi khusus dan mempunyai cetakan khusus. Sebagai contoh, di aplikasi blog, anda mungkin mempunyai tampilan berikut:

- Halaman rumah Blog -- menampilkan sedikit masukan terakhir.
- Halaman "rincian" masukan -- halaman permalink untuk masukan tunggal.
- Halaman arsip berdasarkan-tahun -- menampilkan semua bulan dengan masukan di tahun yang diberikan.
- Halaman arsip berdasarkan-bulan -- menampilkan semua hari dengan masukan di bulan yang diberikan.
- Halaman arsip berdasarkan-hari -- menampilkan semua masukan di hari yang diberikan.
- Tindakan komentar -- menangani penempatan komentar pada masukan yang diberikan.

Di aplikasi jejak pendapat kami, kami akan mempunyai empat tampilan berikut:

- Halaman "index" pertanyaan -- menampilkan sedikit pertanyaan terakhir.
- Halaman "index" pertanyaan -- menampilkan teks pertanyaan, dengan tidak ada hasil tetapi dengan formulir untuk memilih.
- Halaman "hasil" pertanyaan -- menampilkan hasil untuk pertanyaan tertentu.
- Tindakan pilih -- menangani pemilihan untuk pilihan tertentu di pertanyaan tertentu.

Di Django, halaman jaringan dan isi lain dikirimkan oleh tampilan. Setiap tampilan diwakulkan oleh fungsi Python (atau metode, dalam kasus tampilan berdasarkan-kelas). Django akan memilih sebuah tampilan dengan mencoba URL yang diminta (tepatnya, bagian dari URL setelah nama ranah).

Sekarang dalam waktu anda di jaringan anda mungkin menemukan keindahan seperti `ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B`. Anda akan senang mengetahuinya bahwa Django mengizinkan kami lebih elegan *URL patterns* daripada itu.

Sebuah pola URL adalah bentuk umum dari URL - sebagai contoh: `/newsarchive/<year>/<month>/`.

Untuk mendapatkan URL dari tampilan (view), Django menggunakan yang disebut 'URLconfs'. Sebuah URLconf memetakan pola URL ke views.

Tutorial ini menyediakan perintah dasar dalam menggunakan URLconf, dan anda dapat mengacu pada [Pengirim URL](/id/3.2/topics/http/urls/) untuk informasi lebih.

## Menulis tampilan lebih

Sekarang mari kita menambahkan tampilan lebih pada `polls/views.py`. Tampilan ini sedikit berbeda karena mereka mangambil argumen:

*`polls/views.py`*

```python
def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)
```

Ikat tampilan-tampilan baru ini kedalam modul `polls.urls` dengan menambahkan panggilan [`path()`](/id/3.2/ref/urls/#django.urls.path) berikut:

*`polls/urls.py`*

```python
from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/5/results/
    path('<int:question_id>/results/', views.results, name='results'),
    # ex: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]
```

Lihatlah di perambah anda, pada "/polls/34/". Itu akan menjalankan cara `detail()` dan menampilkan ID apapun anda sediakan di URL. Coba "/polls/34/results/" dan "/polls/34/vote/" juga -- ini akan menampilkan hasil placeholder dan halaman pemilihan.

Ketika seseorang meminta sebuah halaman dari situs jaringan anda -- katakan, "/polls/34/", Django akan memuat modul Python `mysite.urls` karena itu ditunjuk dengan pengaturan [`ROOT_URLCONF`](/id/3.2/ref/settings/#std-setting-ROOT_URLCONF). Itu menemukan variabel bernama `urlpatterns` dan melintasi pola dalam urutan. Setelah menemukan kecocokan pada `'polls/'`, itu memotong teks pencocokan (`'polls/'`) dan mengirim teks sisa -- `"34/"` -- ke URLconf 'polls.urls' untuk pengolahan lebih lanjut. Disana itu cocok `'<int:question_id>/'`, menghasilkan dalam sebuah panggilan ke tampilan `detail()` seperti itu:

```
detail(request=<HttpRequest object>, question_id=34)
```

Bagian `question_id=34` datang dari `<int:question_id>`. Menggunakan kurung sudut "menangkap" bagian dari URL dan mengirimnya sebagai argumen kata kunci ke fungsi tampilan. Bagian\`\`question\_id\`\` dari string menentukan nama yang akan digunakan untuk mencirikan pola cocok, dan bagian `int` adalah merubah yang menentukan pola apa yang harus cocok pada bagian ini dari jalur URL. Titik dua (`:`) memisahkan perubah dan pola nama.

## Tulis tampilan yang sebenarnya melakukan sesuatu

Setiap tampilan bertanggung jawab untuk melakukan satu dari dua hal: mengembalikan sebuah obyek [`HttpResponse`](/id/3.2/ref/request-response/#django.http.HttpResponse) mengandung isi untuk halaman diminta, atau menimbulkan sebuah pengecualian seperti [`Http404`](/id/3.2/topics/http/views/#django.http.Http404). Sisanya terserah anda.

Tampilan anda dapat membaca rekaman dari basisdata, atau tidak. Dia dapat menggunakan cetakan sistem seperti DJango -- atau sistem cetakan Phyton pihak-ketiga -- atau tidak. Dia dapat membangkitkan berkas PDF, mengeluarkan XML, membuat berkas ZIP dengan cepat, apapun anda inginkan, menggunakan pustaka Phyton apapun anda inginkan.

Semua Django inginkan adalah [`HttpResponse`](/id/3.2/ref/request-response/#django.http.HttpResponse). Atau sebuah pengecualian.

Karena sangat nyaman, mari kita menggunakan API basisdata Django sendiri, yang akan melingkupi di [Tutorial 2](/id/3.2/intro/tutorial02/). Ini satu tikaman pada tampilan `index()` baru, yang menampilka pertanyaan jejak pendapat 5 terakhir di sistem, dipisahkan oleh koma, menurut tanggal penerbitan:

*`polls/views.py`*

```python
from django.http import HttpResponse

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

# Leave the rest of the views (detail, results, vote) unchanged
```

Tidak ada masalah disini, meskipun: rancangan halaman adalah kode-keras di tampilan. Jika anda ingin merubah cara tampilan halaman, anda harus menyunting kode Phyton ini. Jadi mari kita menggunakan sistem cetakan Django untuk memisahkan rancangan dari Phyton dengan membuat cetakan yang tampilan dapat digunakan.

Pertama, buat direktori disebut `templates` di direktori\`\`polls\`\`. Django akan mencari cetakan disana.

Pengaturan [`TEMPLATES`](/id/3.2/ref/settings/#std-setting-TEMPLATES) proyek anda menggambarkan bagaimana Django akan memuat dan membuat halaman. Pengaturan awal berkas mengkonfigurasikan backend `DjangoTemplates` yang pilihan [`APP_DIRS`](/id/3.2/ref/settings/#std-setting-TEMPLATES-APP_DIRS) adalah disetel ke `True`. Berdasarkan kaidah `DjangoTemplates` mencari untuk subdirektori :cetakan" dalam setiap [`INSTALLED_APPS`](/id/3.2/ref/settings/#std-setting-INSTALLED_APPS).

Dalam direktori `templates` anda telah buat, buat direktori lain disebut `polls`, dan didalamnya buat sebuah berkas dipanggil `index.html`. Dengan kata lain, cetakan anda harus di `polls/templates/polls/index.html`. Karena bagaimana pemuat cetakan `app_directories` bekerja seperti digambarkan diatas, anda dapat mengacu pada cetakan ini dalam Django sebagai `polls/index.html`.

> **Namespacing cetakan**
>
> Sekarang kami *mungkin* dapat lolos dengan menaruh cetakan kami secara langsung dalam `polls/templates` (daripada membuat subdirektori `polls` lain). tetapi itu sebenarnya ide buruk. Django akan memilih cetakan pertama dia temukan yang namanya cocok, dan jika anda memiliki cetakan dengan nama sana dalam aplikasi *berbeda*, Django tidak dapat membedakan mereka. Kami butuh dapat menunjuk Django pada satuyang benar, dan cara terbaik adalah memastikan ini dengan *namespace* merena. Yaitu, dengan menaruh cetakan tersebut didalam direktori *lain* bernama aplikasi itu sendiri.

Taruh kode berikut di cetakan itu:

*`polls/templates/polls/index.html`*

```html+django
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
```

> **Note**
>
> Untuk membuat tutorial lebih pendek, semua contoh cetakan menggunakan HTML tidak lengkap. Dalam proyek anda sendiri anda harus menggunakan [complete HTML documents](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started#anatomy_of_an_html_document).

Sekarang mari kita perbaharui tampilan `index` di `polls/views.py` untuk menggunakan cetakan:

*`polls/views.py`*

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

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))
```

Kode memuat cetakan disebut `polls/index.html` dan melewatkannya ke konteks. Konteks adalah kamus variabel cetakan pemetaan dinamai untuk obyek Phyton.

Muat halaman dengan menunjuk perambah anda pada "/polls/", dan anda akan melihat daftar-bullet mengandung pertanyaan "Ada apa" dari [Tutorial 2](/id/3.2/intro/tutorial02/). Tautan menunjuk ke halaman rincian pertanyaan.

### Jalan pintas: [`render()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.render)

Itu adalah dialek umum untuk memuat cetakan, isi konteks dan mengembalikan sebuah obyek [`HttpResponse`](/id/3.2/ref/request-response/#django.http.HttpResponse) dengan hasil dari cetakan dibuat. Django menyediakan jalan pintas. Disini tampilan `index()` penuh, ditulis kembali:

*`polls/views.py`*

```python
from django.shortcuts import render

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)
```

Catat bahwa sekali kami telah selesaikan ini di semua tampilan ini, kami tidak lagi butuh mengimpor [`loader`](/id/3.2/topics/templates/#module-django.template.loader) dan [`HttpResponse`](/id/3.2/ref/request-response/#django.http.HttpResponse) (anda akan ingin menjaga `HttpResponse` jika anda masih mempunyai potongan cara untuk `detail`, `results`, dan `vote`).

Fungsi [`render()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.render) mengambil permintaan obyek sebagai argumen pertamanya, nama cetakan sebagai argumen keduanya dan kamus sebagai pilihan argumen ketiganya. Dia mengembalikan sebuah obyek [`HttpResponse`](/id/3.2/ref/request-response/#django.http.HttpResponse) dari cetakan diberikan dibangun dengan konteks yang diberikan.

## Menaikkan kesalahan 404

Sekarang, mari kita mengerjakan tampilan rincian pertanyaan -- halaman yang menampilkan teks pertanyaan untuk jejak pertanyaan yang diberikan. Inilah tampilan:

*`polls/views.py`*

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

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})
```

Konsep baru disini: Tapilan memunculkan pengecualian [`Http404`](/id/3.2/topics/http/views/#django.http.Http404) jika sebuah pertanyaan dengan ID diminta tidak ada.

Kami akan berunding apa anda dapat taruh di cetakan `polls/detail.html` itu kemudian, tetapi jika anda suka mendapatkan cepat contoh diatas bekerja, sebuah berkas mengandung hanya:

*`polls/templates/polls/detail.html`*

```html+django
{{ question }}
```

akan membantu anda mulai untuk sekarang.

### Jalan pintas: [`get_object_or_404()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.get_object_or_404)

Adalah dialek umum untuk menggunakan [`get()`](/id/3.2/ref/models/querysets/#django.db.models.query.QuerySet.get) dan menimbulkan [`Http404`](/id/3.2/topics/http/views/#django.http.Http404) jika obyek tidak ada. Django menyediakan jalan pintas. Ini tampilan detail()\`, ditulis kembali:

*`polls/views.py`*

```python
from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})
```

Fungsi [`get_object_or_404()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.get_object_or_404) mengambil model Django sebagai argumen pertamanya dan angka sewenang-wenang dari kata kunci argumen, yang dia melalui fungsi [`get()`](/id/3.2/ref/models/querysets/#django.db.models.query.QuerySet.get) dari pengelola model. Dia menimbulkan [`Http404`](/id/3.2/topics/http/views/#django.http.Http404) jika obyek tidak ada.

> **Filosofi**
>
> Kenapa kami menggunakan fungsi pembantu [`get_object_or_404()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.get_object_or_404) daripada otomatis menangkap pengecualian [`ObjectDoesNotExist`](/id/3.2/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist) pada tingkatan lebih tinggi, atau mempunyai API model menimbulkan [`Http404`](/id/3.2/topics/http/views/#django.http.Http404) daripada [`ObjectDoesNotExist`](/id/3.2/ref/exceptions/#django.core.exceptions.ObjectDoesNotExist)?
>
> Karena itu akan memasangkan lapisan model ke lapisan tampilan. Satu dari terutama sasaran rancangan dari Django adalah merawat sambungan longgar. Beberapa sambungan terkendali diperkenalkan di modul [`django.shortcuts`](/id/3.2/topics/http/shortcuts/#module-django.shortcuts).

Terdapat juga sebuah fungsi [`get_list_or_404()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.get_list_or_404), yang bekerja hanya sebagai [`get_object_or_404()`](/id/3.2/topics/http/shortcuts/#django.shortcuts.get_object_or_404) -- kecuali menggunakan [`filter()`](/id/3.2/ref/models/querysets/#django.db.models.query.QuerySet.filter) daripada [`get()`](/id/3.2/ref/models/querysets/#django.db.models.query.QuerySet.get). Itu memunculkan [`Http404`](/id/3.2/topics/http/views/#django.http.Http404) jika daftar kosong.

## Gunakan sistem cetakan

Kembali ke tampilan `detail()` untuk aplikasi jejak pendapat kami. Diberikan variabel konteks `question`, disini adalah apa cetakan `polls/detail.html` mungkin kelihatan seperti:

*`polls/templates/polls/detail.html`*

```html+django
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
```

Sistem cetakan menggunakan sintaks dot-lookup untuk mengakses atribut variabel. Di contoh dari `{{ question.question_text }}`, pertama Django melakukan lookup perkamusan pada obyek `question`. Kegagalan itu, dia mencoba sebuah atribut lookup -- yang bekerja, di kasus ini. Jika atribut lookup telah gagal, dia akan mencoba list-index lookup.

Pemanggilan-cara terjadi di [`{% for %}`](/id/3.2/ref/templates/builtins/#std-templatetag-for) loop: `question.choice_set.all` adalah ditafsirkan sebagai kode Phyton `question.choice_set.all()`, yang mengembalikan berulang obyek `Choice` dan cocok untuk digunakan di etiket [`{% for %}`](/id/3.2/ref/templates/builtins/#std-templatetag-for).

Lihat [panduan cetakan](/id/3.2/topics/templates/) untuk lebih tentang cetakan.

## Memindahkan URL kode keras di cetakan

Ingat, ketika kami menulis tautan ke pertanyaan di cetakan `polls/index.html`, tautan sebagian dikodekan keras seperti ini:

```html+django
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
```

Masalah dengan kodekeras ini, pendekatan erat adalah bahwa itu menjadi menantang untuk merubah URL pada proyek dengan banyak cetakan. Bagaimanapun, sejak anda menentukan argumen nama dalam fungsi-fungsi [`path()`](/id/3.2/ref/urls/#django.urls.path) dalam modul `polls.urls`, anda dapat memindahkan kepercayaan pada jalur URL khusus ditentukan dalam konfigurasi url anda dengan menggunakan etiket cetakan `{% url %}`.

```html+django
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
```

Cara ini bekerja adalah dengan mencari pengertian URL seperti yang ditentukan di modul `polls.urls`. Anda dapat melihat sebenarnya dimana nama URL dari 'rincian' ditentukan dibawah:

```
...
# the 'name' value as called by the {% url %} template tag
path('<int:question_id>/', views.detail, name='detail'),
...
```

Jika anda ingin merubah URL dari tampilan rinci jejak pendapat ke sesuatu lain, mungkin ke sesuatu seperti `polls/specifics/12/` dibandingkan melakukannya di cetakan (atau cetakan-cetakan) anda akan merubahnya di `polls/urls.py`:

```
...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...
```

## Nama URL namespacing

Proyek tutorial hanya punya satu aplikasi, `polls`. Di proyek Django nyata, mungkin ada lima, sepuluh, dua puluh aplikasi atau lebih. Bagaimana Django membedakan nama URL diantara mereka? Sebagai contoh, aplikasi `polls` mempunyai tampilan `detail`, dan mungkin sebuah aplikasi di proyek sama yaitu untuk sebuah blog. Bagaimana satu membuatnya sehingga Django mengetahui tampilan aplikasi mana untuk membuat untuk sebuah url ketika menggunakan etiket cetakan `{% url %}`?

Jawabannya adalah menambahkan namespace ke URLconf anda. Di berkas `polls/urls.py`, lanjutkan dan tambah `app_name` untuk mengatur namespace aplikasi:

*`polls/urls.py`*

```python
from django.urls import path

from . import views

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

Sekarang rubah cetakan  `polls/index.html` anda dari:

*`polls/templates/polls/index.html`*

```html+django
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
```

untuk menunjuk tampilan rincian namespace:

*`polls/templates/polls/index.html`*

```html+django
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
```

Ketika anda nyaman dengan menulis tampilan, baca [part 4 of this tutorial](/id/3.2/intro/tutorial04/) untuk mempelajari dasar tentang pengolahan formulir dan tampilan umum.
