[Django] 4. 장고 튜토리얼 part.4

김학재·2021년 12월 22일
0

백엔드

목록 보기
5/8
post-thumbnail

오늘은 3에서 이어져서 코드 다이어트를 중점으로 진행한다


Write a minimal form

polls/detail.html 에 간단한 form 을 추가한다.

<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>
  • 각 질문 선택 항목에 대한 라디오 버튼을 표시한다. 각 라디오 버튼의 value는 연관된 질문 선택 항목의 ID. 즉, 라디오 버튼 중 하나를 선택해 폼을 제출하면, POST 데이터인 choice=#(선택한 항목의 ID)을 보낼 것이다.
  • action에서 POST 요청을 통해 서버사이드의 데이터를 변경한다
  • forloop.counter는 for문이 반복을 한 횟수
  • 장고가 제공하는 {% csrf_token %} 을 통해 CSRF(Cross Site Request Forgeries)에 대해 걱정할 필요가 없다

CSRF?
사이트 간 요청 위조. 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 공격

이제 제출된 데이터를 처리하고 그 데이터로 무언가를 수행하는 장고 뷰를 작성

# pols/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,)))
  • request.POST는 키로 전송된 자료에 접근할 수 있도록 해주는 사전과 같은 객체. 여기서 request.post['choice'] 는 선택된 설문의 ID를 문자열로 반환한다
  • 만약 choice가 없으면 KeyError 발생
  • reverse()를 사용해 URL을 하드코딩하지 않도록

설문을 하고 난 뒤에는 결과 페이지로 리다이렉트

# 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})
<!-- 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>

제너릭 뷰 사용하기

여기서 작성한 뷰는 URL에서 전달되니 매개 변수에 따라 데이터베이스에서 데이터를 가져 오는 것과 템플릿을 로드하고 렌더링된 템플릿을 리턴하는 기본 웹 개발의 일반적인 경우를 나타낸다. 장고는 이런 일반적 경우를 위해 제너릭 뷰라는 지름길을 제공한다

  1. URLconf 변환하기
  2. 불필요한 뷰 중 일부 삭제
  3. 제너릭 뷰를 기반으로 새로운 뷰 도입

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

views 수정

# polls/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.

ListView 와 DetailView , 2개의 제너릭 뷰를 사용한다.
각 제너릭 뷰는 model 속성을 사용해 어떤 모델이 적용될 것인지 안다.


확실히 제너릭 뷰를 사용하니까 코드가 좀더 섹시해졌다!

profile
YOU ARE BREATHTAKING

0개의 댓글