[PYTHON] Get started with Django! ~ Tutorial ④ ~

Overview

This is a continuation of Last time. This time, I'll focus on form processing and code reduction with Django.

Form creation

Let's update polls / detail.html created in the tutorial of Last time to include the \

element of 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>

The points are as follows.

--csrf_token is a cross-site request countermeasure provided by Django. --forloop.counter represents the count in Django's forloop. ――After that, on the royal road of Web development, when you submit, the selected name and value values are sent to the specified action destination.

Next, let's modify the destination polls / vote. It will be a modification of view.

polls/views.py


from django.shortcuts import render, get_object_or_404
from .models import Question, Choice  #Added Choice model
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse  #Add reverse


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)


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


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

#ViewAction modified this time
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):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice"
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()

        return HttpResponseRedirect(
            reverse('polls:results', args=(question.id))
        )

Fixed the vote action. I will explain the parts that have not appeared in the explanation so far.

request.POST

We have always specified request as the first argument of the action. It's finally time to use it here.

It is a dictionary-like object that can access the POST data sent by request.POST. In this case, the choice associated with the question object is chosen_set.get, and pk is specified as the get filter.

At that time, if you do request.POST ['choice'], you can access the choice of POST data. (choice is the choice of name attribute)

Similarly, request.GET also exists, but this time we are sending POST data with method = POST, so we will get it with POST.

If there is no POST choice, KeyError will be thrown. The code above checks for KeyError and redisplays the question form with an error message if there is no choice.

After successful POST data processing

This isn't unique to Django, but as a best practice for web development, after successfully processing POST data, you'll be redirected to the target page.

In this case, I'm using HttpResponseRedirect provided by Django to transition to result.

In HttpResponseRedirect, use the reverse function.

The first argument of the reverse function specifies the URLconf pattern. In the second argument, specify the parameters required for the transition.

Creating a result page

Finally, create a result page.

polls/views/py


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


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)


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

#View added this time
def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    context = {
        'question': question
    }
    return render(request, 'polls/results.html', context)


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):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice"
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()

        return HttpResponseRedirect(
            reverse('polls:results', args=(question.id,))
        )

First, add a View. The content is very simple.

Next, create a template.

polls/templates/polls/results.html


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

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

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

The result of the vote is displayed by rotating the loop by the number of choices.

This completes the functions of the app.

Use a generic view

One of the concepts in Python is that "less code is good". Django, of course, uses this idea.

Let's make a simpler view by slightly modifying the views we have created so far.

The view I have developed so far has been the method of The Web development.

Fetch data from the database according to the parameters passed via the URL. ↓ Load the template. ↓ Render and return the template.

The above process is so common that Django has a shortcut as a generic view.

A generic view is an abstraction of common patterns that allows you to write an application without even writing Python code.

Making a generic view a generic view is a step below.

① Convert URLconf. ② Delete the old unnecessary view. ③ Set Django's generic view as the new view.

First, convert the URLconf settings.

polls/urls.py


from django.conf.urls import url

from . import views

app_name = 'polls'

urlpatterns = [
    url(r'^$', views.IndexView.as_view(), name='index'),  #Fixed to go through IndexView class
    url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),  #Modified to go through DetailView class
    url(
        r'^(?P<pk>[0-9]+)/results/$',
        views.ResultsView.as_view(),
        name='results'
    ),   #Modified to go through DetailView class
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

The name of the pattern that matched in the second and third regular expressions has been changed to .

Next, modify the View.

polls/views.py


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

from .models import Question, Choice


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

    def get_queryset(self):
        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):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice"
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()

        return HttpResponseRedirect(
            reverse('polls:results', args=(question.id,))
        )

Here, the View for displaying the template is classified. The parent of the class inherits from the newly imported generic child class.

generic.DetailView

View for detail page.

Each view needs to know which model it works for. This is defined as model proty.

Also, the DetailView generic view is supposed to capture and pass the primary key from the URL with the name ** pk **. That's why I modified it to pk in URLconf earlier.

DetailView General view by default refers to the template `` ` / _detail.html```.

This time it's a unique template name, so you need to explicitly tell the template in the template_name property. (Template_name is not required if you are creating according to the naming convention.)

Also, the context variable uses the model name.

generic.ListView

The default template naming convention for ListView is `` ` / _list.html```.

Since this is our own template, we will use the template_name property.

In addition, the acquired data list by default is stored in the context variable `` ` _list```.

This time, the entire app uses a context variable called latest_question_list.

Therefore, the context_objext_name property overwrites the name of the context variable.

Finally, define the `get_queryset ()` method that stores the value in the context variable.

Summary

This time,

--Form creation --Use of general-purpose view

I explained.

Form creation was no different than frameworks in other languages, but generic views were a bit quirky.

As I remember, as in the tutorial, I was able to select a view from the URL, select a template from the view, render it, and return it as an HttpResponse with almost no code.

You'll have to get used to it, but it's worth remembering. (In the end, React will make a table, so it may not make sense ...)

Next time, I would like to introduce an automated test using the voting app I created.

GitHub

series

-Get started with Django! ~ Tutorial ① ~ -Get started with Django! ~ Tutorial ② ~ -Get started with Django! ~ Tutorial ③ ~ -Get started with Django! ~ Tutorial ④ ~ -Get started with Django! ~ Tutorial ⑤ ~ -Get started with Django! ~ Tutorial ⑥ ~

Recommended Posts

Get started with Django! ~ Tutorial ⑤ ~
Get started with Django! ~ Tutorial ④ ~
Get started with Django! ~ Tutorial ⑥ ~
How to get started with Django
Django 1.11 started with Python3.6
Getting Started with Django 1
Get started with MicroPython
Get started with Mezzanine
Getting Started with Django 2
The easiest way to get started with Django
Get started with influxDB + Grafana
Getting Started with Python Django (1)
Getting Started with Python Django (4)
Getting Started with Python Django (3)
Getting Started with Python Django (6)
Get started with Python! ~ ② Grammar ~
Getting Started with Django with PyCharm
Getting Started with Python Django (5)
Get started with Python! ~ ① Environment construction ~
Link to get started with python
Get started with MicroPython (on macOS)
How to get started with Scrapy
How to get started with Python
Get started with machine learning with SageMaker
Get started with Python in Blender
Get started with the Python framework Django on Mac OS X
Python Django Tutorial (5)
Python Django Tutorial (2)
CRUD GET with Nuxt & Django REST Framework ②
Internationalization with django
django tutorial memo
Here's a brief summary of how to get started with Django
Python Django Tutorial (8)
CRUD GET with Nuxt & Django REST Framework ①
Python Django Tutorial (6)
Start Django Tutorial 1
Get started with the documentation tool Sphinx
Get Started with TopCoder in Python (2020 Edition)
CRUD with Django
Python Django Tutorial (7)
Python Django Tutorial (1)
Python Django tutorial tutorial
How Python beginners get started with Python with Progete
How to get started with laravel (Linux)
Python Django Tutorial (3)
[Blender x Python] Let's get started with Blender Python !!
Python Django Tutorial (4)
Django Getting Started Part 2 with eclipse Plugin (PyDev)
Get started with Python on macOS Big Sur
A layman wants to get started with Python
[Cloud102] # 1 Get Started with Python (Part 1 Python First Steps)
Authenticate Google with Django
Upload files with Django
Development digest with Django
Get started Jupyter Notebook
Python Django tutorial summary
Output PDF with Django
Getting started with Android!
Markdown output with Django
1.1 Getting Started with Python
Getting Started with Golang 2