[Django] tutorial #4-form

23560 단어 djangodjango

form을 이용하여 클라이언트인 사용자로부터 서버쪽으로 데이터를 불러오는 방법을 알아보려고 한다.

저번 part3에서 만든 뷰는 함수기반이었다.
이번에는 class기반으로 뷰를 만들어보려고 한다.
class기반 뷰로 구현하면 소스코드가 줄어든다는 장점이 있다.



간단한 form 만들기


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

코드를 살펴보자면,
데이터를 전송을 위한 요청방식을 post방식으로 하기 위해 post를 명시해주었다.

사용자가 서버로 데이터를 보내주기 위하여 form 태그와 input 태그를 사용하였다.

사용자가 submit typeinput tag을 누르면 해당 url인 polls:vote로 데이터가 전달되고, 해당 url에 연결되어 있는 뷰인 views.vote가 데이터를 처리를 하게 된다.

이때 뷰로 전달되는 데이터는 value값이다.


여기서 해당코드가 사용되는 이유는 다음과 같다

  • {% csrf_token %} : 사이트 위조요청으로, 해킹방지를 위해 사용한다.



우리는 이미 polls 앱을 위해 아래에 나와있는 코드를 포함하는 URLconf를 만들어 두었다.

- polls/urls.py

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

또한 가상으로 만들어 두었던 vote() 함수를
실제로 구현 하기 위해 다음과 같이 수정해준다.

polls/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,)))

다음 코드를 해석해보자면,

  • def vote(request, question_id) : vote 뷰를 호출할때 question_id를 넘겨 받음
  • question = get_object_or_404(Question, pk=question_id) : question_id를 넘겨 받으면 question데이터를 조회
  • except (KeyError, Choice.DoesNotExist):: 조회했을 때 데이터가 없으면 예외가 발생
  • return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) : 예외가 발생했을 때 다시 detail.html(상세페이지)로 respose를 해주며, context 데이터로 questionerror_message를 보내줌
  • else: selected_choice.votes += 1 selected_choice.save() : 조회했을 때 데이터가 있으면 choice를 1 올려주고 저장
  • return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) : reverse함수를 통해 results 뷰를 호출.
    이때 reverse는 뷰 함수에서 url을 하드코딩하지 않도록 도와줌

수정 후 서버를 확인해보면 만들어준 form을 확인 할 수 있다.



results 뷰 만들기


사용자가 설문조사에 설문을 하고나면, vote 뷰는 설문조사 결과 페이지result를 호출한다.

따라서 result 뷰를 작성해보려고 한다.

- polls/view.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})

results 뷰는 question을 조회한 다음, results.html을 결과페이지로 보여준다.
이때 question 데이터가 템플릿으로 전달된다.


따라서 결과페이지를 만들기 위한 템플릿을 작성해준다.

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

뷰로부터 question 데이터를 받아와,
question_text가 제목이 되고
question에 대한 선택지(choice)값들이 반복문을 돌며 리스트로 들어간다.


여기서 처음보는 키워드에 대해 알아보자면,

  • vote{{ choice.votes|pluralize }} : pluralizevotes가 단수인 경우는 단수처리를, 복수인 경우는 복수처리를 하는 장고에서 제공하는 템플릿 기능이다.

위와 같이 처음보는 키워드가 있다면 외우는 것이 아니라 키워드에 대해 구글링 해서 찾아보면 된다


이제 서버에서 설문에 대한 투표를 하고나면, 결과페이지(results)를 확인할 수 있다.



generic 뷰 사용하기


class 기반의 뷰를 generic뷰라고도 한다.

generic 뷰는 일반적인 패턴들에 대해 장고에서 만들어놓았기 때문에 함수형 기반 뷰에 비해 코드량이 적다.

generic 뷰를 사용하기 위해 urlviews 코드를 수정해준다.

- polls/url.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'),
]

generic뷰는 장고에서 미리 작성 된 as_view함수를 호출하여 뷰를 호출하게 된다.

이때 detail이나 results url에서도 int:question_id라는 파라미터 이름을 지우고 pk만 명시해주면 된다.

여기서 pk란 db내의 하나의 열, 즉 하나의 데이터를 구분할 수 있는 값이다. pk값은 중복되지 않는다


- 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.

이렇게 generic 뷰에서는
사용할 템플릿 이름, 해당 템플릿에서 사용할 데이터 모델을 명시해주기만 하면 된다.

이때 context에 넘겨주는 데이터의 이름이 모델이름과 다르다면 context_object_name을 다시 정해주고,
필요한 데이터를 get_queryset 함수를 이용해서 다시 작성해주면 된다.


generic 뷰는 정형화된 작업들이 이미 만들어져 있어 기능들을 익혀 간편하게 사용할 수 있는 것이다.

하지만 장고에 익숙해질때 까지는 함수 기반 뷰를 작성하는 것을 추천한다!



🔎 참고
디장고 공식문서
디장고 공식문서 강의자료

좋은 웹페이지 즐겨찾기