[장고 튜토리얼 따라하기] 첫 번째 장고 앱 작성하기, part4
작성 중...
간단한 폼 쓰기
앞장의 투표 상세 템플릿을 수정하여, 템플릿에 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>
폼의 action속성의 값을 {% url 'polls:vote' question.id %}로 하였는데, action 속성값으로 지정한 url로 요청을 method 속성값의 형식으로 보내겠다는 의미이다.
{% url %} 템플릿 태그는 어제 공부하였다.
method 속성값은, 대부분 POST와 GET이 있다.
[django] method = "get"
GET방식은 클라이언트가 서버에 요청을 할 때, url에 해당 데이터를 담아 보낸다. 도메인 뒤에?id=1&name=ohmygodkimchi 처럼 따라붙는 형태이다(이것을 쿼리 스트링이라고 한다).
이러한 GET방식은 데이터가 url에 노출되기 때문에, 보안에 적절하지 않다고 한다, 따라서 GET방식은 데이터를 조회하는 데 쓰인다고 함.
[django] method = "post"
POST방식은 클라이언트가 서버에 요청을 할 때, 데이터가 노출되지 않는 방식으로, 주로 데이터의 변경을 요청하는 경우에 쓰인다고 함.
[django] {% csrf_token %}
(cross site request forgery) 교차 사이트 요청 위조,사용자가 하지않은 요청을 위조하여 악용하는 것을 방지하는 역할.
폼처리를 할때는 Django가 제공하는 csrf_token 템플릿 태그를 사용한다!
[html] fieldset, legend
views.py에 다음과 같이 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,)))
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을 어떻게 구성하는지 다음 항목을 보셈).
우리는 이 예제에서 HttpResponseRedirect
생성자 안에서 reverse()
함수를 사용하고 있다. 이 함수는 뷰 함수에서 URL을 하드코딩하지 않도록 도와준다. 제어를 전달하기 원하는 뷰의 이름을, URL패턴의 변수부분을 조합해서 해당 뷰를 가리킨다. 여기서 우리는 튜토리얼 3장에서 설정했던 URLconf를 사용하였으며, 이 reverse()
호출은 '/polls/3/results/' 문자열을 반환할 것이다.
여기서 3 은 question.id
값이다. 이렇게 리디렉션된 URL은 최종 페이지를 표시하기 위해 'results' 뷰를 호출한다.
어떤 이가 설문조사에 설문을 하고난 뒤에는, vote() 뷰는 설문조사 결과 페이지로 리다이렉트한다. 그 뷰를 작성해보자.
#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 템플릿을 만든다.
<!--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>
이제, 웹 브라우저에서 /polls/1/ 페이지로 가서, 투표를 해보자. 만약 설문지를 선택하지 않고 폼을 전송했다면, 오류 메시지를 보게 될 것이다.
[django] 경쟁 상태
경쟁 상태 피하기
우리의 vote() 뷰에는 문제가 있다. 먼저 데이터베이스에서 selected_choice 객체를 가져온 다음, votes 의 새 값을 계산하고 나서, 데이터베이스에 다시 저장한다. 만약 여러분의 웹사이트에 두 명의 사용자가 정확하게 같은 시간 에 투표를 할려고 시도할 경우, 잘못될 수 있다. votes 의 조회값이 42라고 할 경우, 두 명의 사용자에게 새로운 값인 43이 계산 되고, 저장된다. 그러나 44가 되야 할 것이다.
제너릭 뷰 사용하기: 적은 코드가 더 좋다.
CRUD기능은 자주 사용되기 때문에, Django는 제네릭 뷰(generic views)라는 것을 지원한다.
자주 사용되는 패턴을 추상화하여, 약간의 설정만 해주면 CRUD 기능을 구현 할 수 있게 된다.
# 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'),
]
[django] 왜 템플릿 변수 이름을 pk로 바꾸었을까?
제네릭 뷰에서 사용하는 템플릿 변수의 이름은 기본값으로
pk
로 설정되어 있기 때문이다.
urls.py에서 정해준 템플릿 변수명을 그대로 사용하고 싶다면, 제네릭 뷰 안에 다음과 같이 설정해준다.
pk_url_kwarg = {뷰에 넘겨주는 변수명}
Author And Source
이 문제에 관하여([장고 튜토리얼 따라하기] 첫 번째 장고 앱 작성하기, part4), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hamkua/장고-튜토리얼-따라하기-첫-번째-장고-앱-작성하기-part4저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)