voting/urls.py 파일 수정
...
urlpatterns = [
# /voting/
path('', views.IndexView.as_view(), name='index'),
# /voting/99
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
# /voting/99/results/
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
# /voting/99/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
함수형 뷰 -> 클래스형 뷰
voting/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 voting.models import Question, Choice
# ListView를 상속받는 경우 객체가 들어있는 리스트를 구성해서
# 이를 컨텍스트 변수로 템플릿 시스템으로 넘겨주면 되는데
# 이 리스트가 모든 테이블의 레코드 구성이라면 모델 클래스만 지정
# 아니면 get_queryset() 메소드 오버라이딩하여 원하는 리스트 구성
class IndexView(generic.ListView):
template_name = 'voting/index.html'
# 컨텍스트 변수명 지정
context_object_name = 'latest_question_list'
def get_queryset(self):
# 최근 생성된 질문 5개 반환
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
# Question 테이블로부터 특정 레코드를 가져와 컨텍스트 변수 구성
# 컨텍스트 변수명은 디폴트 값을 사용하고
# object와 모델명 소문자인 quesiton 둘 다 가능
model = Question
template_name = 'voting/detail.html'
class ResultsView(generic.DetailView):
# Choice가 아닌 Question 객체를 넘겨준다.
# Question 객체를 구해서 해당 객체와 FK로 연결된 Choice를 구한다.
# 이 로직은 results.html 템플릿 파일에서
# question.choice_set.all() 구문으로 구현되어 있다.
model = Question
template_name = 'voting/results.html'
def vote(request, question_id):
# Choice 테이블을 검색한다. request.POST는 제출된 폼의 데이터를 담고 있는
# 객체로서 key로 그 값을 구할 수 있다.
# request.POST['choice']는 폼 데이터에서 키가 'choice'에 해당하는 값인
# choice.id를 스트링으로 리턴한다.
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
# 'choice'라는 키가 없으면 KeyError 익셉션 발생
# 검색 조건에 맞는 객체가 없으면 Choice.DoesNotExist 익셉션 발생
except (KeyError, Choice.DoseNotExist):
# 익셉션이 발생하면 render() 함수에 의해 question과 error_message
# 컨텍스트 변수를 detail.html 템플릿으로 전달
# 에러 메세지와 함께 질문 항목 폼을 다시 보여줘서 재입력할 수 있도록 함
return render(request, 'voting/detail.html', {
'question': question,
'error_message': "You didn't select a choice",
})
else:
selected_choice.votes += 1
# 변경 사항 Choice 테이블 저장
selected_choice.save()
# POST 데이터를 정상적으로 처리했으면
# 항상 HttpResponseRedirect를 반환하여 리다이렉션 처리
# 최종적으로 vote() 뷰 함수는 리다이렉트할 타겟 URL을 담은
# HttpResponseRedirect 객체 반환
return HttpResponseRedirect(reverse('voting:results', args=(question.id,)))
상속 기능 추가하기
base.html을 이미 코딩했기 때문에 이를 상속 받는 base_voting.html 템플릿 파일을 만들고 기존 각 템플릿 파일에서 base_voting.html 템플릿을 상속받으면 된다.
base_voting.html
{% extends 'base.html' %}
<title>{% block title %}Voting Application Site{% endblock %}</title>
{% block sidebar %}
{{ block.super }}
<ul>
<li><a href="/voting/">Voting_Home</a></li>
</ul>
{% endblock%}
voting/index.html 수정
{% extends "base_books.html" %}
{% block content %}
<h2>Voting Question List</h2>
...
{% endblock content %}
voting/detail.html 수정
{% extends "base_voting.html" %}
{% block content %}
<h1>{{ question.question_text }}</h1>
...
{% endblock content%}
voting/results.html 수정
{% extends "base_voting.html" %}
{% block content %}
...
{% endblock content%}
settings.py 파일에 로깅 설정을 해주고 로거를 취득해서 로그 기록을 원하는 곳에서 로거의 메소드를 호출하면 된다.
로깅 설정에서 배웠던 코드 그대로 적용하면 된다.
settings.py 파일 수정
...
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
# [로그 메시지를 기록한 시간], 로그 레벨 이름
# [로거이름:라인번호], 로그 메시지 순서로 출력
'verbose': {
'format': "[%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt': "%d%b%Y %H:%M:%S"
}
},
'handlers': {
# DEBUG 이상의 메시지를 파일로 출력해주는 FileHandler 사용
# 로그가 기록되는 파일 이름은 D:web_programing\web_programing\logs\testsite.log
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filters': os.path.join(BASE_DIR, 'logs', 'testsite.log'),
# 위에서 정의한 verbose 포맷터 사용
'formatter': 'verbose'
},
},
'loggers': {
# testsite 로거 이름 대신에 voting 로거 이름으로 바꿨다.
# views.py 파일에서 __name__변수로 로거를 취득하기 위함이다.
'voting': {
'handlers': ['file'],
'level': 'DEBUG',
},
}
}
voting/views.py 수정
# logging 추가
import logging
# getLogger(__name__) 메소드를 호출해서 voting.views 로거 객체 취득
logger = logging.getLogger(__name__)
...
def vote(request, question_id):
# 로거 객체의 debug 메소드를 호출해서 로거에게
# DEBUG 수준으로 로그 레코드를 생성하도록 요청
# 로거는 앞에서 수정한 settings.py 파일의 로깅 설정에 따라
# file 핸들러를 사용하여 로그 메시지를 기록한다.
logger.debug("vote().question_id: %s" % question_id)
...
logs 디렉토리가 없다면 만들어줘야 한다.
출처: Django로 배우는 파이썬 웹 프로그래밍(기초) - 김석훈님