Django 입문 form, genetic view

1c2·2023년 7월 23일
0

Django

목록 보기
8/12

Django form 사용

클래스 기반 view(제너릭 뷰)로 구현하면 함수 기반 view와 기능은 같으나 소스코드가 줄어드는 효과가 있다. detail.html을 수정하여 form요소를 포함시켜 보자.

polls/templates/pools/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"> <!--submit을 누르게 되면 url(polls:vote)을 호출하며, value값이 넘어가게 된다-->
</form>

제출된 data를 처리하고 무언가를 수행하는 view를 만들 것이다. 그 전에 URLconf를 만들었다.

polls/urls.py

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

views.py에 가상으로 만들었던 vote함수를 구현해 보도록 하자.

views.py

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 += 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,)))

코드의 설명은 Django의 공식문서를 참고하여 천천히 이해해 보도록 하자.

  • request.POST 는 키로 전송된 자료에 접근할 수 있도록 해주는 사전과 같은 객체입니다. 이 경우, request.POST['choice'] 는 선택된 설문의 ID를 문자열로 반환합니다. request.POST 의 값은 항상 문자열들입니다.

  • Django는 같은 방법으로 GET 자료에 접근하기 위해 request.GET 를 제공합니다 – 그러나 POST 요청을 통해서만 자료가 수정되게하기 위해서, 명시적으로 코드에 request.POST 를 사용하고 있습니다.

  • 만약 POST 자료에 choice 가 없으면, request.POST['choice'] 는 KeyError 가 일어납니다. 위의 코드는 KeyError 를 체크하고, choice가 주어지지 않은 경우에는 에러 메시지와 함께 설문조사 폼을 다시보여줍니다.

  • 설문지의 수가 증가한 이후에, 코드는 일반 HttpResponse 가 아닌 HttpResponseRedirect 를 반환하고, HttpResponseRedirect 는 하나의 인수를 받습니다: 그 인수는 사용자가 재전송될 URL 입니다. (이 경우에 우리가 URL을 어떻게 구성하는지 다음 항목을 보세요).

  • 위의 Python 주석에서 지적한 바와 같이 POST 데이터를 성공적으로 처리 한 후에는 항상 HttpResponseRedirect 를 반환해야 합니다. 이 팁은 Django에만 국한된 것이 아니라 일반적으로 좋은 웹 개발 관행입니다.

  • 우리는 이 예제에서 HttpResponseRedirect 생성자 안에서 reverse() 함수를 사용하고 있습니다. 이 함수는 뷰 함수에서 URL을 하드코딩하지 않도록 도와줍니다. 제어를 전달하기 원하는 뷰의 이름을, URL패턴의 변수부분을 조합해서 해당 뷰를 가리킵니다. 여기서 우리는 튜토리얼 3장에서 설정했던 URLconf를 사용하였으며, 이 reverse() 호출은 아래와 같은 문자열을 반환할 것입니다.
    /polls/3/results/
다음과 같이 detail.html이 바뀐 것을 확인할 수 있다.

detail.html에서 선택된 choice를 url(polls:votes)로 보내고, 해당 url은 polls/view.py의 vote함수로 보낸다. 다음 해당 함수는 result url로 결과를 보내고, result url은 views.py의 result함수로 들어간다.

그러면 result함수를 만들어보자.

polls/view.py

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

polls/templates/polls/results.html

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

<ul>
{% for choice in question.choice_set.all %}.
  	<!--단수이면 vote를 복수이면 votes를 띄우도록 하는 pluralize기능-->
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> 
{% endfor %}
</ul>

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

투표의 결과를 보여주는 html코드이다.

위 페이지에서 vote를 누르게 되면 이렇게 투표의 결과를 보여주는 result.html을 띄워주는 것을 확인할 수 있다.

generic view

URLconf를 다음과 같이 수정한다.

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

두 번째와 세 번째 패턴의 경로 문자열에서 일치하는 패턴들의 이름이 <question_id> 에서 <pk> 로 변경되었다.

views.py또한 수정한다.

views.py

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.

다음과 같이 전체적인 코드가 줄어든 것을 확인할 수 있다.

0개의 댓글