# Documentazione Django 6.0.7

# Scrivere la tua prima applicazione in Django, parte 4

Questo tutorial inizia dove si è interrotto il tutorial Tutorial 3. Continuiamo con l’applicazione web dei questionari e ci focalizzeremo sul processamento dei form e sul ridurre il nostro codice.

Dove trovare aiuto:

Se hai difficoltà a completare questo tutorial, per favore vai alla sezione Getting Help delle FAQ.

## Scrivere un modulo minimale

Aggiorniamo il nostro template per i dettagli del questionario («polls/detail.html») dall’ultimo tutorial, così che il template contenga un elemento HTML `<form>`:

`polls/templates/polls/detail.html`

    <form action="{% url 'polls:vote' question.id %}" method="post">
    {% csrf_token %}
    <fieldset>
        <legend><h1>{{ question.question_text }}</h1></legend>
        {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
        {% 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 %}
    </fieldset>
    <input type="submit" value="Vote">
    </form>

Una rapida carrellata:

- Il template di cui sopra mostra un radio button per ogni scelta del sondaggio. Il `valore` di ogni radio button è l’ID associato alla scelta. Il `nome` di ogni radio button è `"choice"`. Questo significa che quando qualcuno seleziona uno dei radio button ed invia il form, manderà il dato `choice=#` in POST, dove # è l’ID della scelta selezionata. Questo è il concetto alla base dei form HTML.

- Impostiamo la `action` del form a `{% url 'polls:vote' question.id %}` ed impostiamo `method="post"`. Usare `method="post"` (in opposizione a `method="get"`) è molto importante perchè il submit di questo form altererà i dati a lato server. Tutte le volte che crei un form che altera i dati a lato server, usa `method="post"`. Questo consiglio non si applica solo a Django; è una buona pratica di sviluppo web, in generale.

- `forloop.counter` indica quante volte il tag `for` ha attraversato suo loop

- Siccome stiamo creando un form POST (che può avere l’effetto di modificare i dati), dobbiamo preoccuparci delle Cross Site Request Forgeries. Per fortuna, non ce ne dobbiamo preoccupare troppo, perchè Django ci offre un sistema che ci aiuta molto a proteggerci. In breve, tutti i form POST che sono indirizzati dalle url interne devono usare il tag `{% csrf_token %}`.

Adesso, creiamo una view Django che gestisce i dati in submit e ci fa qualcosa. Ricorda che nel Tutorial 3, abbiamo creato una URLconf per l’applicazione sondaggio che include questa linea:

`polls/urls.py`

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

Abbiamo anche creato una implementazione dummy della funzione `vote()`. Creiamone una versione reali. Aggiungi quanto di seguito a `polls/views.py`:

`polls/views.py`

    from django.db.models import F
    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 = F("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,)))

Questo codice comprende alcuni aspetti non ancora trattati in questo tutorial:

- `request.POST` è un oggetto simil-dizionario che ti lascia accedere ai dati in submit per nome di chiave. In questo caso, `request.POST['choice']` restituisce l’ID della scelta selezionata, come stringa. I valori `request.POST` sono sempre stringhe.

  Nota che Django ti offre anche `request.GET` per accedere ai dati in GET allo stesso modo – ma stiamo esplicitamente usando `request.POST` nel nostro codice, per assicurarci che i dati vengano alterati solo con una chiamata POST.

- `request.POST['choice']` solleverà `KeyError` se `choice` non è presente tra i dati in POST. Il codice sopra controlla `KeyError` e mostra di nuovo il form della domanda con un messaggio di errore se `choice` non viene fornita.

- `F("votes") + 1` instructs the database to increase the vote count by 1.

- Dopo aver incrementato il conteggio delle scelte, il codice restituisce una `HttpResponseRedirect` piuttosto che una normale `HttpResponse`. `HttpResponseRedirect` prende un solo argomento: la URL alla quale l’utente sarà redirezionato (vedi il punto seguente per sapere come costruiamo la URL in questo caso).

  Come il commento Python qui sopra fa notare, dovresti sempre restituire un `HttpResponseRedirect` dopo aver avuto a che fare con successo con dati in POST. Questo consiglio non riguarda nello specifico solo Django; è una buona pratica per lo sviluppo web, in generale.

- In questo esempio stiamo usando la funzione `reverse()` nel costruttore `HttpResponseRedirect`. Questa funzione aiuta ad evitare di dover codificare la URL nella funzione view. E” dato il nome della view a cui vogliamo passare il controllo e la porzione della URL relativa alla variabile che punta alla URL. In questo caso, usando la URLconf che abbiamo impostato nel Tutorial 3, questa chiamata `reverse()` restituirà una stringa come:

      "/polls/3/results/"

  dove il `3` è il valore di `question.id`. Questa URL redirezionata poi chiamerà la view `'results'` per mostrare la pagina finale.

Come menzionato in Tutorial 3, `request` è un oggetto `HttpRequest`. Per saperne di più sugli oggetti `HttpRequest`, vedi la documentazione su request e response.

Dopo che qualcuno ha votato una domanda, la vista `vote()` reindirizza alla pagina dei risultati per la domanda. Scriviamo quella vista:

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

Questa è praticamente la stessa view di `detail()` dal Tutorial 3. L’unica differenza è il nome di template. Metteremo a posto questa ridondanza più tardi.

Ora, crea un template `polls/results.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>

Adesso, vai su `/polls/1/` nel browser e vota il sondaggio. Dovresti vedere la pagina dei risultati che si aggiorna ogni volta che voti. Se invii il form senza scegliere una risposta, dovresti vedere il messaggio di errore.

## Usa viste generiche: meno codice è meglio

Le view `detail()` (dal Tutorial 3) e `results()` sono molto brevi – e, come menzionato sopra, ridondanti. La view `index()`, che mostra una lista di sondaggi, è simile.

Queste views rappresentano un caso comune di sviluppo web di base; prendere dei dati dal database in accordo con un parametro passato via URL, caricare un template e restituire il template per il rendering. Dal momento che questo è molto comune, Django fornisce una scorciatoia, chiamata sistema «generic views».

Generic views abstract common patterns to the point where you don’t even need to write Python code to write an app. For example, the `ListView` and `DetailView` generic views abstract the concepts of «display a list of objects» and «display a detail page for a particular type of object» respectively.

Convertiamo la nostra app dei sondaggi affinchè utilizzi il sistema delle view generiche, in modo da poter cancellare un sacco di codice. Dovremo fare la conversione in qualche step.

1. Converti la URLconf.

2. Cancella alcune delle vecchie, non necessarie viste.

3. Introduci nuove visualizzazioni basate sulle generic views di Django.

Continua a leggere per i dettagli.

Perchè il code-shuffle?

Solitamente, quando scrivi un’applicazione in Django, dovresti valutare quali viste generiche vanno bene per il tuo problema, invece di ristrutturare il codice a metà strada Ma questa guida era intenzionalmente focalizzato fino ad ora, sullo scrivere le viste nel modo più difficile, per mettere a punto i concetti principali.

Dovresti conoscere la matematica di base prima di iniziare a usare una calcolatrice.

### Correggere la URLconf

Prima di tutto, apri la URLconf `polls/urls.py` e cambiala così:

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

Note that the name of the matched pattern in the path strings of the second and third patterns has changed from `<question_id>` to `<pk>`. This is necessary because we’ll use the `DetailView` generic view to replace our `detail()` and `results()` views, and it expects the primary key value captured from the URL to be called `"pk"`.

### Correggere le viste

Poi, andremo a rimuovere le nostre vecchie view `index`, `detail` e `results` per usare le view generiche di Django. Per farlo, apri il file `polls/views.py` e cambialo così:

`polls/views.py`

    from django.db.models import F
    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.
        ...

Each generic view needs to know what model it will be acting upon. This is provided using either the `model` attribute (in this example, `model = Question` for `DetailView` and `ResultsView`) or by defining the `get_queryset()` method (as shown in `IndexView`).

Di default, la view generica `DetailView` usa un template chiamato <app name>/<model name>_detail.html`. Nel nostro caso, dovrebbe usare il template `"polls/question_detail.html"`. L’attributo `template_name` è utile per usare un template specifico invece del nome di template generato automaticamente. Specifichiamo anche il `template_name` per la list view `results` – questo ci assicura che la view dei risultati e la view dei dettagli abbiamo una interfaccia diversa quando vengono mostrate, anche se, dietro le quinte, sono entrambe `DetailView`.

In modo simile, la view generica `ListView` usa un template di default chiamato `<app name>/<model name>_list.html`; usiamo `template_name` per dire a `ListView` di usare il nostro template esistente `"polls/index.html"`.

Nella parte precedente di questo tutorial, i template sono stati forniti con un contesto che contiene le variabili `question` e `latest_question_list`. Per `DetailView` la variabile `question` viene fornita automaticamente – visto che stiamo usando un modello Django (`Question`), Django è in grado di determinare un nome appropriato per la variabile di contesto. Comunque, per ListView, la variabile di contesto generata automaticamente è `question_list`. Per sovrascrivere questo, forniamo l’attributo context_object_name`, specificando che vogliamo usare `latest_question_list` al suo posto. Come approccio alternativo, puoi cambiare i tuoi template per rispecchiare le nuove variabili di contesto di default – ma è molto più facile dire a Django di usare le variabili che vuoi.

Esegui il server e usa la tua nuova applicazione di sondaggio basata su visualizzazioni generiche.

Per i dettagli completi sulle viste generiche, vedere la: doc:generic views documentation </topics/class-based-views/index>.

Quando hai dimestichezza con i moduli e le visualizzazioni generiche, leggi la parte 5 di questo tutorial per scoprire come testare la nostra app per i sondaggi.