voting 애플리케이션 - 클래스 뷰로 변경

URLconf 코딩

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

]

View 코딩

함수형 뷰 -> 클래스형 뷰

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

Template 코딩

상속 기능 추가하기
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로 배우는 파이썬 웹 프로그래밍(기초) - 김석훈님

좋은 웹페이지 즐겨찾기