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

Take a look in your browser, at „/polls/34/”. It’ll run the `detail()` function and display whatever ID you provide in the URL. Try „/polls/34/results/” and „/polls/34/vote/” too – these will display the placeholder results and voting pages.

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

There’s a problem here, though: the page’s design is hardcoded in the view. If you want to change the way the page looks, you’ll have to edit this Python code. So let’s use Django’s template system to separate the design from Python by creating a template that the view can use.

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>

The problem with this hardcoded, tightly-coupled approach is that it becomes challenging to change URLs on projects with a lot of templates. However, since you defined the `name` argument in the `path()` functions in the `polls.urls` module, you can remove a reliance on specific URL paths defined in your url configurations by using the `{% url %}` template tag:

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