이 글은 [파이썬 웹 프로그래밍](저자 김석훈, 출판사 한빛미디어) 교재를 보고 공부하며 정리한 글임.
4.5 클래스형 뷰
장고에서는 뷰를 함수로도 작성 가능하고, 클래스로도 작성 가능함.
-> 클래스형 뷰가 장점이 많음. 중급, 고급 개발자를 위해선 잘 익혀두자!
4.5.1 클래스형 뷰의 시작점
URLconf에서 함수형 뷰가 아닌 클래스형 뷰를 사용한다는걸 표시해줘야함.
예시로 MyView라는 클래스형 뷰를 사용한다고 하면 URLconf가 아래처럼 됨
# urls.py
from django.urls import path
from myapp.views import MyView
urlpatterns = [
path('about/', MyView.as_view()),
]
as_view() 메소드 : 진입 메소드. 클래스의 인스턴스를 생성하고 그 인스턴스의 dispatch() 메소드를 호출함.
dispatch() 메소드는 요청을 검사하여 GET, POST 등 어떤 HTTP 메소드로 요청되었는지 알아내고,
인스턴스 내에서 해당 이름을 갖는 메소드로 요청을 중계해줌.
그럼 MyView 클래스형 뷰를 만들어보자. 함수형 뷰 만들었던 views.py에 코딩하면됨.
#views.py
from django.http import HttpResponse
from django.views.generic import View
class MyView(View):
def get(self, request):
#뷰 로직 작성
return HttpResponse('result')
MyView 클래스는 View 클래스를 상속받고 있는거다.
View 클래스에는 as_view() 와 dispatch() 가 정의되어 있다.(그래서 따로 우리가 정의할 필요없이 바로 사용가능한것!)
4.5.2 클래스형 뷰의 장점 - 효율적인 메소드 구분
만약 GET 메소드 처리하는 로직 작성한다고 해보자.
함수형 뷰는
def my_view(request):
if request.method == 'GET':
return HttpResponse('result')
이런식이라면 클래스형 뷰는
class MyView(View):
def get(self, request):
return HttpResponse('result')
이렇게 if 형을 쓰지 않아도 된다. -> 내부에 dispatch() 메소드가 있기 때문임.
4.5.3 클래스형 뷰의 장점 - 상속 기능
# some_app/urls.py
from django.urls import path
from some_app.views import AboutView
urlpatterns = [
path('about/', AboutView.as_view()),
]
#some_app/views.py
from django.vies.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
generic 뷰를 상속 받아서 클래스형 뷰를 작성하는 예제이다.
about/ 이라는 URL로 요청이 들어오면 about.html 이라는 템플릿을 보여주는거다.
TemplateView 제네릭뷰를 좀 더 간단한 다른 방식으로 사용하려면
urls.py 파일에서 about.html 파일을 아예 지정해줄 수 있다. views.py에 작성할 필요없이!
아래와 같이 urls.py를 작성해주면 된다.
#some_app/urls.py
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('about/', TemplateView.as_view(template_name="about.html")),
]
4.5.4 클래스형 제네릭 뷰
장고에서 제공하는 제네릭 뷰를 4가지로 분류한다면
- Base View : 뷰 클래스 생성, 다른 제네릭 뷰의 부모 클래스 제공
View, TemplateView, RedirectView
- Generic Display View : 객체의 리스트를 보여주거나 특정 객체의 상세정보 보여줌
ListView, DatailView
- Generic Edit View : 폼을 통해 객체를 생성, 수정, 삭제 하는 기능 제공
FormView, CreateView, UpdateView, DeleteView
- Generic Date View : 날짜 기반 객체의 연/월/일 페이지로 구분해서 보여줌
ArchiveIndexView, YearArchiveView ..등
4.5.5 클래스형 뷰에서 폼 처리
3가지 경우
- 최초의 GET : 사용자에게 처음으로 폼을 보여줌
- 유효한 데이터를 가진 POST : 데이터를 처리함. 주로 리다이렉트 처리됨
- 유효하지 않은 데이터를 가진 POST : 보통 에러 메세지와 함께 폼이 다시 출력됨
이러한 폼 처리 과정을 FormView 라는 제네릭 뷰로 제공함.
-> get(), post() 메소드 정의가 불필요해짐
from .forms import MyFrom
from django.views.generic edit import FormView
class MyFormView(FormView):
form_class = MyForm
template_name = 'form_template.html'
success_url = '/thanks/'
def form_valid(self, form):
return super(MyFormView, self).form_valid(form)
이거 작성할때 4가지 유의하자
- form_class : 사용자에게 보여줄 폼을 정의한 forms.py 파일내의 클래스명
- template_name : 폼을 사용하여 렌더링할 템플릿 파일이름
- success_url : MyFormView 처리가 정상적으로 완료되었을때 리다이렉트시킬 URL
- form_valid() 함수 : 유효한 데이터로 처리할 로직 코딩, super() 함수를 사용하면 success_url로 지정된 URL로 리다이렉션 처리됨
4.6 로그 남기기
4.6.1 로거
로거 : 로그 메세지를 처리하기 위해 메세지를 담아주는 저장소
로거들은 로그 레벨을 가짐.
- NOTSET 0
- DEBUG 10
- INFO 20
- WARNING 30
- ERROR 40
- CRITICAL 50
로그 레코드 : 로거에 저장되는 메세지
로그레코드가 로거에 도착 -> 로그 레코드의 로거의 로그레벨 비교
->로그 레코드 레벨이 같거나 더 높으면 메세지 처리하고
더 낮으면 무시됨
-> 로거는 핸들러에게 메세지 넘겨줌
4.6.2 핸들러
핸들러 : 로거에 있는 메세지에 무슨 작업을 할지 결정하는 엔진. 여러개일 수 있음.
로그 동작을 정의. 로그레코드가 핸들러의 로그 레벨보다 낮으면 핸들러는 무시함
4.6.3 필터
로그 레코드가 로거에서 핸들러로 넘어갈때
필터를 사용해서 로그 레코드에 추가적인 제어를 할 수 있음
(ex : 특정 소스에서 오는 메세지만 핸들러로 넘긴다던지.. 로그 레코드 보내기 전에 수정한다던지..)
4.6.4 포맷터
로그 레코드는 최종적으로 텍스트로 표현됨.
포맷터는 텍스트로 표현할 때 사용할 포맷을 지정해준다.
4.6.5 로거 사용 및 로거 이름 계층화
로그를 기록하기 위해서는 앞서 설명한 로거, 핸들러, 필터, 포맷터 등을 설정한 후에, 코드 내에서 로깅 메소드를 호출하면 됨.
로깅 설정은 settings.py 파일에 작성.
# mysite/settings.py
LOGGING = {
'version':1,
'disable_existing_loggers': Flase,
'handlers':{
'console':{
'class':'logging.StreamHandlers',
},
},
'loggers':{
'mylogger':{
'handlers':['console'],
'level': 'INFO'
},
},
}
그리고 로그 메세지를 기록하기위해 views.py 에 로거를 취득하고 로깅메소드를 호출하는 코드 작성.
# some_app/views.py
import logging
logger = logging.getLogger('mylogger')
def my_view(request, arg1, arg):
if bad_mojo:
logger.error('Something went wrong')
logging.getLogger() 메소드 호출하면 로거 객체를 얻을 수 있다.
로그를 모듈단위로 기록하고 싶으면 로거를 구분하는 이름을 도트(.) 방식으로 입력
logger = logger.getLogger('project.interesting.stuff')
-> 로거를 계층화 한거임. project.interesting.stuff 의 로거는 project.interesting 이 되는 것
로거의 계층화가 필요한 이유?
최상단 루트 로거에서 핸들러 하나만을 만들어도 하위 로거의 모든 로깅 호출을 잡을 수 있다.
로거 객체는 각 로그 레벨별로 로깅 호출 메소드를 가지고 있다.
- logger.debug()
- logger.info()
- logger.warning()
- logger.error()
- logger.critical()
- logger.log()
- logger.exception()
4.6.6 장고의 디폴트 로깅 설정
파이썬의 로깅 라이브러리는 딕셔너리 방식을 디폴트로 사용.
4.6.8 로깅 설정- 디폴트 설정 유지
장고의 디폴트 설정을 유지하면서 개발자의 로깅을 설정.(동작하는 방식은 오버라이딩으로 변경 가능하긴 하다.)
장고의 디폴트 설정을 무시하고 새롭게 로깅 방식을 설정할 수도 있다(로깅 시스템에 익숙해 졌다면?)
이미 정의한 로거들을 사용하지 않고 새롭게 로거들을 설정할 때에는 다음과 같이 작성 하면 됨
LOGGING_CONFIG = NOHNE
LOGGGIG = {
원하는 내용으로 로깅 컴포넌트 설정함
}
import logging.config
logging.congfig.dictConfig(LOGGING)
로깅에 대한 내용이 잘 설명 되어 있는 곳이다. 읽어보면서 생각을 정리해보자.