728x90
반응형

2021.06.09 - [BACK-END/Django] - [Django] (투표하기)-모델 만들기 / makemigrations / migrate

 

[Django] (투표하기)-모델 만들기 / makemigrations / migrate

2021.06.08 - [BACK-END/Django] - [Django] admin 커스터마이징 / MySQL 연동 [Django] admin 커스터마이징 / MySQL 연동 2021.06.07 - [BACK-END/Django] - [Django] 파이참 설치하고 장고 개발 서버 실행하기 [..

hello-ming.tistory.com

 

전 포스팅에서 Questions와 Choices 모델을 만들었다. 지난 포스팅에 이어 계속해서 Django 공식 튜토리얼을 토대로 참고 자료로 내용을 덧붙여 포스팅하도록 하겠다.


이번에 추가할 뷰는 총 네 가지이다.

 

1. index : 최근의 질문들을 표시한다.

 

2.  detail : 질문 내용과 투표할 수 있는 서식을 표시한다.

 

3.  results : 특정 질문에 대한 결과를 표시한다.

 

4.  vote : 질문에 대해 선택을 할 수 있는 투표 기능을 제공한다.

 

from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404

# Create your views here.
from django.urls import reverse

from polls.models import Question, Choice


# 함수 형 view , 클래스형 view
def index(request):
    latest_question_list = Question.objects.all().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)
    return render(request,'polls/detail.html',{'question':question})

def results(request, question_id):
    question = get_object_or_404(Question,pk=question_id)
    return  render(request, 'polls/results.html',{'question':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):
        #질문 투표 형식을 다시 표시함
        return render(request, 'polls/detail.html',{
            'question':question,
            'error_message':'당신은 선택한 항목이 없습니다.'
        })
    else :
        selected_choice.votes +=1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results',args=(question.id,)))

 

[코드 설명]

 

index

새로운 index() 뷰 하나를 호출했을 때, 시스템에 저장된 최소한 5개의 투표 질문이 콤마로 분리되어, 발행일에 따라 출력된다.

polls/index.html 템플릿을 불러 온 후, context를 전달한다. context는 템플릿에서 쓰이는 변수명과 Python 객체를 연결하는 사전형 값이다. 


vote

Post 데이터를 성공적으로 처리가 된 후 HttpResponseRedirect를 반환한다.

이것은 사용자가 back 버튼을 누르면 두 번 글이 등록되는 것을 방지하기 위해서 이다.

reverse는 url을 해당 url을 찾을 때 사용함 기본적으로 url을 가지고 이동하는데 반대로 url을 찾아야 하므로 거꾸로라는 reverse를 호출한다.


 

URLconf를 통해 URL 패턴을 뷰에 연결하는 작업이 필요하므로 path() 함수를 이용해 호출을 추가하여 작성한 새로운 뷰를 polls.urls 모듈에 연결한다.

 

polls/urls.py를 생성해서 내용을 추가한다.

from django.urls import path

from polls import views

app_name = 'polls'
urlpatterns=[
    path('',views.index, name='index'), #views안의 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'),
]

 

runserver를 돌려 /admin으로 접속한다.

Choices와 Questions에 각각 +add 를 눌러 항목을 만든다.

 

 

그리고 또한 MySQL에서 Question을 생성할 수 있다.

 

 


디자인을 위해 templates를 작성할 것이다.

 

polls - templates - polls 디렉토리를 생성한 후 detail.html,  index.html, results.html을 생성한다.

 

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>가능한 여론(polls)가 없어요</p>
{% endif %}

 웹 브라우저에 "/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>

■ 각 질문 선택 항목를 라디오 버튼으로 표시한다. 각 라디오 버튼의 value는 연관된 질문 선택 항목의 ID이다. 각 라디오 버튼의 name은 "choice"이므로 누군가가 라디오 버튼 중 하나를 선택해 폼을 제출하면, POST 데이터인 choice=#을 보낼 것이다. 여기서 #은 선택한 항목의 ID이다.

 

■ 폼의 action을 {%url 'polls:vote' question.id%}로 설정하고 method="post"를 설정한다(metho="get"이 아니라). method="post"를 사용한다는 점이 굉장히 중요하다. 그 이유는 폼의 송신은 서버쪽의 데이터의 갱신으로 연결되기 때문이다. 서버쪽의 데이터를 갱신하는 폼을 작성할 경우는 method="post"을사용하자. 이것은 장고 고유의 특징이 아닌 웹 개발에 전체에 통용되는 것이다.

 

■ forloop.counter은 for태그의 루프가 몇 번 실행되었는지 표시하는 값이다.

 

■ (데이터가 변조될 위험이 있는) POST 폼을 작성하고 있으므로, Cross Site Request Forgeries에 대해 신경써야할 필요가 있다. 다행히도, 장고가 이에 대해 대응하기 위해 사용하기 쉬운 구성품을 제공하고 있으므로 그렇게 걱정할 필요는 없다. 짧게 말하자면, 내부 URL 대상의 모든 POST 폼은 {% csrf_tokem %} 템플릿 태그를 써야한다. 

 

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 %}">다시 투표하실래요?</a>

 

웹 브라우저의 /polls/1/ 페이지에서 투표를 할 수 있을 것이고 투표할 때마다 값이 반영된 결과 페이지를 확인할 수 있다.

 

만약 투표를 하지 않았다면 오류 메시지를 확인할 수 있을 것이다. 

[ 동작 화면 ]

 

728x90
반응형

+ Recent posts