# Django 4.2.9.dev20231204082947 - dokumentacja

# Pisanie pierwszej aplikacji Django, część 3.

Ten tutorial zaczyna się, gdzie skończył się Tutorial 2. Kontynuujemy z aplikacją web-ankiet i skupimy się na tworzeniu publicznego interfejsu – „widoków”.

Gdzie szukać pomocy:

Jeśli masz trudności w przejściu tego tutorialu, przejdź do sekcji Uzyskiwanie pomocy często zadawanych pytań.

## Przegląd

Widok jest „typem” strony internetowej w tworzej aplikacji Django, który w ogólności służy określonej funkcji i ma określony szablon. Na przykład w aplikacji blogowej, możesz mieć następujące widoki:

- Strona główna bloga – wyświetla kilka najnowszych wpisów.
- Strona „szczegółów” wpisu – strona na pojedynczy wpis.
- Roczna strona archiwum – wyświetla wszystkie miesiące z wpisami w danym roku.
- Miesięczna strona archiwum – wyświetla wszystkie dni z wpisami w danym miesiącu.
- Dzienna strona archiwum – wyświetla wszystkie wpisy z danego dnia.
- Komentowanie – obsługuje dodawanie komentarzy do danego wpisu.

W naszej aplikacji ankietowej będziemy mieć następujące cztery widoki:

- Strona „główna” pytań – wyświetla klika najnowszych pytań.
- Strona „szczegółów” pytania – wyświetla treść pytania, bez wyników, ale z formularzem do głosowania.
- Strona „wyników” pytania – wyświetla wyniki dla wybranego pytania.
- Akcja głosowania – obsługuje głosowanie na konkretny wybór w wybranym pytaniu.

W Django strony i inna treść są dostarczane przez widoki. Każdy widok jest reprezentowany przez funkcję Pythona (lub metodę, w przypadku widoków opartych na klasach). Django wybierze widok sprawdzając URL, który jest zażądany (aby być dokładnym, część URL-a po nazwie domeny).

Teraz w swoim czasie w Internecie można natknąć się na takie piękności jak `ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B`. Ucieszysz się wiedząc, że Django pozwala nam na znacznie bardziej eleganckie wzorce adresów URL niż ten.

Wzorzec URL jest ogólną formą URL-a – na przykład: `/newsarchive/<year>/<month>/`.

Aby przejść z URL-a do widoku, Django używa czegoś, co znane jest jako «URLconfs». URLconf mapuje wzorce URL na widoki.

Ten tutorial zawiera podstawowe instrukcje użytkowania URLconfów. Możesz przejść do URL dispatcher po więcej informacji.

## Pisanie kolejnych widoków

Teraz dodajmy kilka więcej widoków do `polls/views.py`. Te widoki są nieco inne, bo biorą argument:

`polls/views.py`

    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)

Podłącz te nowe widoki do modułu `polls.urls` dodając następujące wywołania `path()`:

`polls/urls.py`

    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"),
    ]

Spójrz na „/polls/34/” w swojej przeglądarce. Uruchomi to metodę `detail()` i wyświetli jakiekolwiek ID podasz w URL-u. Spróbuj też „/polls/34/results/” i „/polls/34/vote/” – te wyświetlą strony wyników i głosowania.

Kiedy ktoś żąda strony z twojej witryny – powiedzmy „/polls/34/”, Django załaduje moduł `mysite.urls` Pythona, ponieważ jest wskazany przez ustawienie `ROOT_URLCONF`. Odnajduje on zmienną nazwaną `urlpatterns` i trawersuje po kolei wzorce. Po odnalezieniu dopasowania w `'polls/'`, odcina pasujący tekst (`„polls/”`) i wysyła pozostały tekst – `„34/”` – do URLconfa «polls.urls» do dalszego przetwarzania. Tam dopasowuje go do `'<int:question_id>/'`, co kończy się wywołaniem widoku `detail()` w ten sposób:

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

Część `question_id=34` pochodzi z `<int:question_id>`. Użycie nawiasów trójkątnych „wyłapuje” część URL-a i wysyła ją jako argument do funkcji widoku. Część `:question_id>` ciągu znaków określa nazwę, która zostanie użyta, aby zidentyfikować dopasowany wzorzec, a część `<int:` jest konwerterem, który określa jakie wzorce powinny pasować do tej części ścieżki URL. Dwukropek (`:`) rozdziela nazwy konwertera i wzorca.

## Pisanie widoków, które faktycznie coś robią

Każdy widok jest odpowiedzialny za zrobienie jednej z dwóch rzeczy: zwrócenie obiektu `HttpResponse` zawierającego treść dla żądanej strony lub rzucenie wyjątku takiego jak `Http404`. Reszta zależy od ciebie.

Twój widok może czytać rekordy z bazy danych lub nie. Może używać systemu szablonów takiego jak Django – lub innego systemu szablonów Python – lub nie. Może generować plik PDF, zwracać XML, tworzyć w locie plik ZIP, cokolwiek chcesz, używając dowolnych bibliotek Pythona, których chcesz.

Wszystko, czego chce Django, to ten `HttpResponse`. Lub wyjątek.

Użyjmy własnego bazodanowego API Django, które omówiliśmy w Tutorialu 2, ponieważ jest wygodne. Tu jest próba w nowym widoku `index()`, która wyświetla ostatnie 5 pytań ankietowych w systemie, oddzielone przecinkami, według daty publikacji:

`polls/views.py`

    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

Chociaż mamy tutaj problem: design strony jest zahardcodowany w widoku. Jeśli chcesz zmienić sposób, w jaki wygląda strona, będziesz musiał zmienić ten kod Pythona. Użyjmy systemu szablonów Django, aby oddzielić design od Pythona tworząc szablon, który będzie mógł użyć widok.

Najpierw stwórzmy katalog o nazwie `templates` w twoim katalogu `polls`. Django będzie tam szukał szablonów.

Ustawienie `TEMPLATES` twojego projektu opisuje jak Django będzie ładował i renderował szablony. Domyślny plik ustawień konfiguruje backend `DjangoTemplates`, którego opcja `APP_DIRS` jest ustawiona na `True`. Według konwencji `DjangoTemplates` szuka podkatalogu „templates” w każdej z `INSTALLED_APPS`.

W katalogu `templates`, który właśnie stworzyłeś, stwórz kolejny katalog o nazwie `polls` i w tym katalogu stwórz plik o nazwie `index.html`. Innymi słowy, twój szablon powinien być pod `polls/templates/polls/index.html`. Ponieważ loader szablonów `app_directories` działa jak opisano powyżej, możesz odwoływać się do tego szablonu w Django `polls/index.html`.

Przestrzeń nazw szablonu

Teraz moglibyśmy poradzić sobie umieszczając nasze szablony bezpośrednio w `polls/templates` (zamiast tworzyć kolejny podkatalog `polls`), ale w rzeczywistości byłoby to złym pomysłem. Django wybierze pierwszy szablon, którego nazwa się zgadza i jeśli miałbyś szablon o takiej samej nazwie w innej aplikacji, Django nie byłoby w stanie ich odróżnić. Musimy być w stanie wskazać Django odpowiedni szablon i najlepszym sposobem, aby to zapewnić jest znamespace’owanie ich. To znaczy umieszczenie tych szablonów w innym katalogu nazwanym dla samej aplikacji.

Umieść następujący kod w tym szablonie:

`polls/templates/polls/index.html`

    {% 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 %}

Informacja

Aby skrócić tutorial, wszystkie przykłady szablonów używają niepełnego HTML-a. W swoich projektach powinieneś używać pełnych dokumentów HTML.

Teraz zaktualizujmy nasz widok `index` w `polls/views.py`, aby używał szablonu:

`polls/views.py`

    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))

Ten kod ładuje szablon o nazwie `polls/index.html` i przekazuje mu kontekts. Kontekst jest słownikiem mapującym nazwy zmiennych szablonu do obiektów Pythona.

Załaduj stronę kierując swoją przeglądarkę na „/polls/”. Powinnieneś zobaczyć wypunktowaną listę zawierającą pytanie „What’s up” z Tutoriala 2. Link kieruje do strony szczegółów pytania.

### Skrót: `render()`

To popularny idiom do ładowania szablonu, wypełniania kontekstu i zwracania obiektu `HttpResponse` z wynikiem wyrenderowanego szablonu. Djano daje skrót. Tutaj jest pełen widok `index()`, napisany na nowo:

`polls/views.py`

    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)

Zwróć uwagę, że kiedy zrobiliśmy to w tych wszystkich widokach, nie potrzebujemy już importować `loader` i `HttpResponse` (będziesz chciał zatrzymać `HttpResponse` jeśli wciąż masz zaczątki metod dla `detail`, `results` i `vote`).

Funkcja `render()` bierze obiekt request jako swój pierwszy argument, nazwę szablonu jako drugi argument i słownik jako swój opcjonalny trzeci argument. Zwraca obiekt `HttpResponse` danego szablonu wyrenderowany z danym kontekstem.

## Zgłaszanie błędu 404

Teraz zawalczmy z widokiem szczegółów pytania – strony, która wyświetla treść pytania dla danej ankiety. Tutaj jest widok:

`polls/views.py`

    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})

Nowy koncept tutaj: Widok zgłasza wyjątek `Http404` jeśli pytanie o żądanym ID nie istnieje.

Omówimy trochę później co mógłbyś umieścić w tym szablonie `polls/detail.html`, ale jeśli chcesz, aby powyższy przykład szybko zaczął działać, plik zawierający tylko:

`polls/templates/polls/detail.html`

    {{ question }}

pomoże ci wystartować na teraz.

### Skrót: `get_object_or_404()`

To popularny idiom do używania `get()` i zgłaszania `Http404` jeśli obiekt nie istnieje. Django daje skrót. Tutaj jest widok `detail()` przepisany:

`polls/views.py`

    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})

Funkcja `get_object_or_404()` bierze model Django jako swój pierwszy argument i wyznaczoną wcześniej liczbę arguementów nazwanych, które przekazuje do funkcji `get()` menadżera modelu. Zgłasza `Http404` jeśli obiekt nie istnieje.

Filozofia

Dlaczego używamy funkcji pomocniczej `get_object_or_404()` zamiast automatycznie wyłapywać wyjątki `ObjectDoesNotExist` na wyższym poziomie lub powodując, że API modelu będzie zgłaszać `Http404` zamiast `ObjectDoesNotExist`?

Ponieważ to związałoby warstwę modelu z warstwą widoku. Jednym z najważniejszych celów projektowych Django było utrzymywania luźnych więzów. Trochę kontrolowanego wiązania jest wprowadzone w module `django.shortcuts`.

Jest też funkcja `get_list_or_404()`, która działa tak samo jak `get_object_or_404()` – z wyjątkiem używania `filter()` zamiast `get()`. Zgłasza wyjątek `Http404` gdy lista jest pusta.

## Użyj systemu szablonów

Wracamy do widoku `detail()` naszej aplikacji ankietowej. Mając zmienną kontekstową `question`, tak mógłby wyglądać nasz szablon `polls/detail.html`:

`polls/templates/polls/detail.html`

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

System szablonów używa składni kropkowego lookupu, aby dostać się do atrybutów zmiennej. W przykładzie `{{ question.question_text }}`, najpierw Django robi słownikowy lookup na obiekcie `question`. Gdy to się nie uda, próbuje atrybutowego lookupu – który w tym przypadku działa. Gdyby atrybutowy lookup się nie powiódł, spróbowałoby lookupu przez indeks listy.

Wywoływanie metod dzieje się w pętli `{% for %}`: `question.choice_set.all` jest interpretowane jako kod Pythona `question.choice_set.all()`, który zwraca iterator obiektów `Choice` i jest odpowiedni do użycia w tagu `{% for %}`.

Zobacz przewodnik po szablonach po więcej informacji na temat szablonów.

## Usuwanie wstawionych na sztywno URL-i z szablonów

Pamiętasz, kiedy wpisaliśmy link do pytania w szablonie `polls/index.html`, link był częściowo wpisany na sztywno, w ten sposób:

    <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

Problem z tym podejściem z hardkodowaniem jest taki, że staje się niezwykle trudna zmiana URL-i w projektach z dużą liczbą szablonów. Jednakże, odkąd zdefiniowałeś nazwany argument w funkcjach `path()` w module `polls.urls`, możesz usunąć poleganie na poszczególnych ścieżkach URL zdefiniowanych w twoich konfiguracjach URL-i używając szablonowego taga `{% url %}`:

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

Działa to przez sprawdzenie definicji URL-a według specyfikacji w module `polls.urls`. Możesz zobaczyć poniżej, gdzie dokładnie nazwa «detail» jest zdefiniowana w URL-u:

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

Jeśli chcesz zmienić URL widoku szczegółów ankiety na jakiś inny, na przykład na coś w stylu `polls/specifics/12/`, zamiast robić to w szablonie (lub szablonach), zmieniłbyś to w `polls/urls.py`:

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

## Przestrzenie nazw dla nazw URL-i

Projekt tutorialowy ma tylko jedną aplikację, `polls`. W prawdziwych projektach Django może być pięć, dziesięć, dwadzieścia lub więcej aplikacji. Jak Django odróżnia nazwy URL pomiędzy nimi? Na przykład aplikacja `polls` ma widok `detail` i tak samo mogłaby mieć aplikacja blogowa w tym samym projekcie. Jak zrobić, aby Django wiedziało, której aplikacji widok stworzyć dla urla w przypadku użycia taga `{% url %}`?

Odpowiedzią jest dodanie przestrzeni nazw do twojego URLconfa. W pliku `polls/urls.py` dodaj `app_name`, aby ustawić przestrzeń nazw aplikacji:

`polls/urls.py`

    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"),
    ]

Teraz zmień swój szablon `polls/index.html` z:

`polls/templates/polls/index.html`

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

aby wskazywał na widok szczegółów w przestrzeni nazw:

`polls/templates/polls/index.html`

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

Jeśli czujesz się dobrze z pisaniem widoków, przeczytaj część czwartą tego tutoriala, aby dowiedzieć się podstaw o przetwarzaniu formularzy i widokach generycznych.