본문 바로가기

문돌이 존버/Django 스터디

장고(Django), 뷰 장식자(View decorator) 및 Generic Date Views

반응형
본 글은 Holix의 "리액트와 함께 장고 시작하기 Complete" 강의를 듣고 작성한 일지입니다.

장식자(Decorators)

장식자란 어떤 함수를 감싸는(Wrapping) 함수입니다. 이는 장고의 기능이 아니라 파이썬에서 지원되는 기능으로 전체 로직은 아래와 같습니다.

장식자 함수는 내부 함수를 호출해서 그에 따른 응답을 리턴하거나 혹은 내부 함수를 호출하지 않고 자신이 요청에 따른 응답을 직접 하기도 합니다. 그럼 몇 가지 장고의 기본 Decorators를 살펴보겠습니다.

1. django.views.decorators.http
- require_http_methods, require_GET, require_POST, require_safe
- 지정 method가 아닐 경우, HttpResponseNotAllowed 응답 반환

2. django.contrib.auth.decorators
- user_passes_test: 지정 함수가 False를 반환하면 login_url로 redirect
- login_required: 로그아웃 상황에서 login_url로 redirect
- permission_required: 지정 퍼미션이 없을 때 login_url로 redirect

3. django.contrib.admin.views.decorators
- staff_member_required: staff_member가 아닐 경우 login_url로 이동

위에서 login_required 장식자를 한 번 적용해보겠습니다.

# views.py
from django.utils.decorators import method_decorator

# 클래스에 장식자 함수 wrapping
@method_decorator(login_required, name='dispatch')
class PostListView(ListView):
  model = Post
  paginate_by = 10

post_list = PostListView.as_view()
클래스 멤버함수에는 method_decorator 를 활용
dispatch() 함수는 클래스에 요청(모든 request 요청)이 왔을 때 처리

장식자를 사용하지 않고도 동일한 기능을 수행하게 하는 방법이 여러 가지 있습니다. 아래처럼 클래스를 상속하거나 아예 CBV를 파라미터로 감싸는 방법도 있습니다.

# views.py
class PostListView(LoginRequiredMixin, ListView): # LoginRequiredMixin 클래스 상속
    model = Post
    paginate_by = 10
    
post_list = PostListView.as_view()

# 파라미터로 전달
# post_list = login_required(ListView.as_view(model=Post, paginate_by=10))

Generic Date Views

1. ArchiveIndexView: 지정 날짜필드 역순으로 정렬된 목록
2. YearArchiveView: 지정 year 년도의 목록
3. MonthArchiveView: 지정 year/month 월의 목록
4. WeekArchiveView: 지정 year/week 주의 목록
5. DayArchiveView: 지정 year/month/day 일의 목록
6. TodayArchiveView: 오늘 날짜의 목록
7. DateDetailView: 지정 year/month/day 목록 중에서 특정 pk의 detail
- DetailView와 비교했을 때, url에 year/month/day를 쓰고자 할 경우에 유용

공통 옵션
allow_future (디폴트: False)
- 현재시간 이후의 Record는 제외

ArchiveIndexView

- 최신 목록을 보고자 할 때 사용, 필요한 URL 인자는 없음

옵션
- model
- date_field: 정렬 기준 필드
- date_list_period (디폴트: 'year')

디폴트 template_name_suffix: '_archive.html'

Context
- latest: QuerySet
- date_list: 등록된 Record의 년도 목록
# views.py
from django.views.generic.dates import ArchiveIndexView

post_archive = ArchiveIndexView.as_view(model=Post, date_field='created_at')

현재 작성된 포스트는 모두 2021년으로 되어 있기 때문에 shell을 통해 년도를 바꿔주도록 하겠습니다.

python manage.py shell

from blog1.models import Post
post_list = Post.objects.all()

import random

for post in post_list:
     year = random.choice(range(1990, 2020))
     month = random.choice(range(1, 13))
     post.created_at = post.created_at.replace(year=year, month=month)
     post.save()
 Post.objects.all().values_list('created_at')
 Post.objects.all().values_list('created_at__year') # flat=True <- 튜플이 아닌 리스트 원소로 리턴

<!--post_archive.html-->
<h2>latest</h2>

{{ latest }}

<h2>date_list</h2>

{% for date in date_list %}
    {{ date.year }}
{% endfor %}

YearArchiveView

필요한 URL 인자: 'year'

옵션
- model, date_field
- date_list_period (디폴트: 'month')
- make_object_list (디폴트: False)
  (거짓일 경우, object_list를 비움)

디폴트 template_name_suffix: '_archive_year.html'

Context
- year, previous_year, next_year
- date_list: 전체 Record의 월 목록
- object_list
# views.py
ost_archive_year = YearArchiveView.as_view(model=Post, date_field='created_at', make_object_list=True)
# 앱/urls.py
class YearConverter:
    regex = r"20\d{2}"

    def to_python(self, value): # url로부터 추출한 문자열을 뷰에 넘겨주기 전에 변환
        return int(value)
    def to_url(self, value): # url reverse시 호출
        return str(value)

register_converter(YearConverter, 'year') # converter 이름 지정

app_name = 'blog1'

urlpatterns = [
    ...,
    path('archive/<year:year>', views.post_archive_year, name='post_archive_year'),
]
<!--post_archive_year.html-->
<h2>latest</h2>

{{ latest }}

<h2>date_list</h2>

{% for date in date_list %}
    {{ date.year }}
{% endfor %}

MonthArchiveView

필요한 URL 인자: 'year', 'month'

옵션
- month_format (디폴트: '%b')
  (숫자 포맷은 '%m')

디폴트 template_name_suffix: '_archive_month.html'

Context
- month, previous_month, next_month
- date_list: 전체 Record의 날짜 목록
- object_list

WeekArchiveView

필요한 URL 인자: 'year', 'month'

옵션
- week_format
  ('%U': 한 주의 시작을 일요일로 지정 <- 디폴트)
  ('%W': 한 주의 시작을 월요일로 지정)

디폴트 template_name_suffix: '_archive_week.html'

Context
- week, previous_week, next_week
- date_list: 전체 Record의 날짜 목록
- object_list

DayArchiveView

필요한 URL 인자: 'year', 'month', 'day'

옵션
- month_format (디폴트: '%b')
  (숫자 포맷은 '%m')

디폴트 template_name_suffix: '_archive_day.html'

Context
- day, previous_day, next_day
- date_list: 전체 Record의 날짜 목록
- object_list
참조
https://github.com/django/django/blob/main/django/views/generic/dates.py
728x90
반응형