일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 크레인 인형뽑기 게임
- 프로그래머스
- 보행자 천국
- Python
- pycon
- 호텔 방 배정
- 티스토리 open api
- 카카오 인턴
- 티스토리
- 불량 사용자
- 트라이 #trie #알고리즘
- jdbc
- Open API
- bulk update
- Tistory
- 가사 검색
- Spring Boot
- CleanCode
- 징검다리 건너기
- trie
- 튜플
- 알고리즘
- 트라이
- Today
- Total
택시짱의 개발 노트
10장. 클래스 기반 뷰의 모범적인 이용 본문
10장. 클래스 기반 뷰의 모범적인 이용
장고의 뷰는 요청 객체를 받고 응답 객체를 반환하는 내장 함수다.
함수 기반 뷰에서는 뷰 함수 자체가 내장 함수이고, 클래스 기반 뷰에서는 뷰 클래스가 내장 함수를 반환하는 as_view() 클래스 메서드를 제공한다.
django.views.generic.View에서 해당 매커니즘이 구현되며 모든 클래스 기반 뷰는 이 클래스를 직간접적으로 상속받아 이용한다.
10.1 클래스 기반 뷰의 가이드 라인
- 뷰 코드의 양은 적으면 적을수록 좋다.
- 뷰 안에서 같은 코드를 반복적으로 이용하지 말자.
- 뷰는 프레젠테이션 로직에서 관리하도록 하자. 비즈니스 로직은 모델에서 처리하자. 매우 특별한 경우에는 폼에서 처리하자.
- 뷰는 간단 명료해야 한다.
- 403, 404, 500 에러 핸들링에 클래스 기반 뷰는 이용하지 않는다. 대신 함수 기반 뷰를 이용하자
- 믹스인은 간단 명료해야 한다.
10.2 클래스 기반 뷰와 믹스인 이용하기
클래스 기반 뷰 (Classy Class-Based Views) 참고 링크 ( 링크 )
- 프로그래밍에서 mixin은 실체화된 클래스가 아니라 상속해줄 기능들을 제공하는 클래스를 의미한다.
- 프로그래밍 언어에서 다중 상속을 해야 할 때 믹스인을 쓰면 클래스에 향상된 기능과 동작을 추가할 수 있다.
mixin을 사용하여 자체 뷰 클래스를 구성할 때 다음 규칙을 권장
- Django가 제공하는 기본 뷰 클래스는 항상 오른쪽으로 이동한다.
- mixin은 기본 뷰에서부터 왼쪽으로 진행한다.
- mixin은 파이썬의 기본 객체 타입을 상속 해야한다. 즉 다른 클래스에 상속 되어서는 안된다.
from django.views.generic import TemplateView
class FreshFruitMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["has_fresh_fruit"] = True
return context
class FruitFlavorView(FreshFruitMixin, TemplateView):
template_name = "fruity_flavor.html"
- FruitFlavorView 클래스는 FreshFruitMixin과 TemplateView를 상속 받고 있다.
- TemplateView가 장고에서 제공하는 기본 클래스이기 때문에 가장 오른쪽에 위치
- FreshFruitMixin은 Mixin이기 때문에 기본 클래스의 왼쪽에 위치
10.3 어떤 장고 제네릭 클래스 기반 뷰를 어떤 태스크에 이용할 것인가?
Django의 각 클래스 기반 뷰의 목적과 이름을 나열
장고 클래스 기반 뷰/ 제네릭 클래스 기반 뷰의 이용에 대한 세 가지 의견
- '제네릭 뷰의 모든 종류를 최대한 이용'
- 장고를 사용하는 이유는 개발 작업의 양을 최소화 하는데 목적이 있다는 철학에 기반을 둔 그룹의 주장. 작업량을 최소화 하기 위해 제네릭 뷰가 제공하는 모든 종류의 뷰를 최대한 이용하기를 장려. 이러한 방법으로 다수의 프로젝트를 빠르고 쉽게 개발, 유지 보수 하는데 큰 성공을 거둠
- '심플하게 django.views.generic.View 하나로 모든 뷰를 다 처리'
- 장고의 기본 클래스 기반 뷰로도 충분히 원하는 기능을 다 소화할 수 있다. 난해한 태스크들에 대해서 기본 클래스를 사용하는 방법이 매우 효율적임을 발견
- '뷰를 정말 상속할 것이 아닌 이상 그냥 무시'
- 프로젝트를 시작할 때 읽기 쉽고 이해하기도 쉬운 함수 기반 뷰로 시작한 후 클래스 기반 뷰가 반드시 필요한 상황이 되었을 때만 클래스 기반 뷰를 이용하자. 그렇다면 어떠한 상황에서 클래스 기반 뷰를 이용해야 할까? 여러 뷰에서 재사용할 수 있는 코드의 양이 꽤 많은 경우이다.
10.4 장고 클래스 기반 뷰에 대한 일반적인 팁
10.4.1 인증된 사용자에게만 장고 클래스 기반 뷰/제네릭 클래스 기반 뷰 접근 가능하게 하기
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import DetailView
from .models import Flavor
class FlavorDetailView(LoginRequiredMixin, DetailView):
model = Flavor
10.4.2 뷰에서 유효한 폼을 이용하여 커스텀 액션 구현하기
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
def form_valid(self, form):
# 커스텀 로직이 이곳에 위치
# from_valid()의 반환형은 django.http.HttpResponseRedirect가 된다.
return super().form_valid(form)
10.4.3 뷰에서 부적합한 폼을 이용하여 커스텀 액션 구현하기
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
def form_invalid(self, form):
# 커스텀 로직이 이곳에 위치
# form_invalid()는 django.http.HttpResponse를 반환한다.
return super().form_invalid(form)
10.4.4 뷰 객체 이용하기
- 콘텐츠를 렌더링하는 데 클래스 기반 뷰를 이용한다면 자체적인 메서드와 속성을 제공하는 뷰 객체를 이용하여 다른 메서드나 속성에서 호출이 가능하게 하는 방법을 고려해 볼 수 있다.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.functional import cached_property
from django.views.generic import UpdateView, TemplateView
from .models import Flavor
from .tasks import update_user_who_favorited
class FavoriteMixin:
@cached_property
def likes_and_favorites(self):
"""likes와 favorites의 딕셔너리를 반환"""
likes = self.object.likes()
favorites = self.object.favorites()
return {
"likes": likes,
"favorites": favorites,
"favorites_count": favorites.count(),
}
class FlavorUpdateView(LoginRequiredMixin, FavoriteMixin, UpdateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
def form_valid(self, form):
update_user_who_favorited(
instance=self.object,
favorites=self.likes_and_favorites['favorites']
)
return super().form_valid(form)
class FlavorDetailView(LoginRequiredMixin, FavoriteMixin, TemplateView):
model = Flavor
def form_valid(self, form):
likes_and_favorites = self.likes_and_favorites
---
{# flavors/base.html #}
{% extends "base.html" %}
{% block likes_and_favorites %} <ul>
<li>Likes: {{ view.likes_and_favorites.likes }}</li>
<li>Favorites: {{ view.likes_and_favorites.favorites_count}}</li>
</ul>
{% endblock likes_and_favorites %}
- 다양항 flavors 앱 템플릿에서 해당 속성을 호출할 수 있다는 장점이 있다.
10.5 제네릭 클래스 기반 뷰와 폼 사용하기
from django.db import models
from django.urls import reverse
class Flavor(models.Model):
class Scoops(models.IntegerChoices)
SCOOPS_0 = 0
SCOOPS_1 = 1
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
scoops_remaining = models.IntegerField(choices=Scoops.choices, default=Scoops.SCOOPS_0)
def get_absolute_url(self):
return reverse("flavors:detail", kwargs={"slug": self.slug})
10.5.1 뷰 + 모델폼 예제
- 가장 단순하고 일반적인 장고 폼 시나리오.
- FlavorCreateView: 새로운 종류의 아이스크림을 추가하는 폼
- FlavorUpdateView: 기존 아이스크림을 수정하는 폼
- FlavorDetailView: 아이스크림 추가와 변경을 확정하는 폼
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, DetailView, UpdateView
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
class FlavorUpdateView(LoginRequiredMixin, UpdateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
class FlavorDetailView(DetailView):
model = Flavor
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, DetailView, UpdateView
from .models import Flavor
class FlavorActionMixin:
fields = ['title', 'slug', 'scoops_remaining']
@property
def success_msg(self):
return NotImplemented
def form_valid(self, form):
messages.info(self.request, self.success_msg)
return super().form_valid(form)
class FlavorCreateView(LoginRequiredMixin, FlavorActionMixin, CreateView):
model = Flavor
success_msg = "Flavor created!"
class FlavorUpdateView(LoginRequiredMixin, FlavorActionMixin, UpdateView):
model = Flavor
success_msg = "Flavor updated!"
class FlavorDetailView(DetailView):
model = Flavor
믹스인은 object를 상속해야 한다.
FlavorActionMixin은 이미 존재하는 믹스인이나 뷰를 상속하지 않고 파이썬의 object 타입을 상속한다는 점을 알아두자. 믹스인은 가능한 한 아주 단순한 상속의 연결이 되어야 한다는 것을 잊지 말자.
10.6 django.views.generic.View 이용하기
각 HTTP 메서드를 중첩된 if 문으로 처리하는 함수 기반 뷰를 작성하거나 get_context_data()와 form_valid() 메서드 뒤에 숨어 있는 HTTP 메서드들이 위치한 클래스 기반 뷰를 작성하는 대신 이 메서드들에 직접 접근할 수 있다면 어떨까?
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.shortcuts import render, redirect
from django.views.generic import View
from .forms import FlavorForm
from .models import Flavor
class FlavorView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
# Flavor 객체의 디스플레이를 처리
flavor = get_object_or_404(Flavor, slug=kwargs['slug'])
return render(request, "flavors/flavor_detail.html", {"flavor": flavor})
def post(self, request, *args, **kwargs):
# Flavor 객체의 업데이트를 처리
flavor = get_object_or_404(Flavor, slug=kwargs['slug'])
form = FlavorForm(request.POST, instance=flavor)
if form.is_valid():
form.save()
return redirect("flavors:detail", flavor.slug)
---
커스텀 로직
class FlavorPDFView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
# 종류 할당
flavor = get_object_or_404(Flavor, slug=kwargs['slug'])
# 응답 생성
response = HttpResponse(content_type='application/pdf')
# PDF 스트림 생성 후 응답에 할당
response = make_flavor_pdf(response, flavor)
return response
요약
- 클래스 기반 뷰와 폼 패턴을 잘 다루어 둔다면 또 다른 강력한 무기를 습득하게 된다.!
'Django(장고)' 카테고리의 다른 글
18장. 장고 코어 모듈을 교체할 때 주의점 (0) | 2021.11.03 |
---|---|
django rest framework에 질문 이메일을 보내봤다. (0) | 2021.10.29 |
9장. 함수 기반 뷰의 모범적인 이용 (0) | 2021.10.10 |
4장. 장고 앱 디자인의 기본 (0) | 2021.09.02 |
3장. 어떻게 장고 프로젝트를 구성할 것인가 (0) | 2021.09.02 |