본문으로 바로가기

https://docs.djangoproject.com/en/3.0/topics/class-based-views/

 

Class-based views | Django documentation | Django

The Django Software Foundation deeply values the diversity of our developers, users, and community. We are distraught by the suffering, oppression, and systemic racism the Black community faces every day. We can no longer remain silent. In silence, we are

docs.djangoproject.com

 

현재까지 우리가 views를 코딩한 방식은 function based view였습니다.

def all_rooms(request):
    # url에서 가져옴
    page = int(request.GET.get("page", 1))
    
    page_size = 10
    limit = int(page_size * page)
    offset = int(limit - page_size)
    
    all_rooms = models.Room.objects.all()[offset:limit]
    
    # rendering
    return render(request, "rooms/home.html", context={"rooms": all_rooms})

from django.views.generic import ListView, DetailView

그런데 fucntion based view가 아닌 class based view도 존재합니다.

일반적으로 django.views에서 View를 상속받은 후 각 http method에서 취할 동작을 지정합니다.

from django.views import View

# Create your views here.
class LoginView(View):
    def get(self, request):
        pass

    def post(self, request):
        pass

 

일반 view 외에 generic-display와 함께 곁들여서 사용해봅시다.

 

 

 

🚀 generic-display

 

generic-display: https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-display

 

타입 언어의 generic과 관련 없이, 일반적(general)인 display를 빠르게 작성하는 목적으로 사용됩니다.

 

generic-display는 DetailView, ListView로 나뉩니다. 무엇을 상속할 것인지는 구현하고자하는 것에 따라 결정합시다.

The two following generic class-based views are designed to display data. On many projects they are typically the most commonly used views.

 

이것들은 django의 여러 가지를 합쳐서 추상화해놓은, 편리한 창입니다.

공식 문서를 보면 Ancestors 목록을 살펴볼 수 있습니다.

 

우리가 사용하려는 ListView는 

이런 것들을 기반으로 만들어진 것입니다.

 

따라서 ListView는 위 ancestor의 메서드들을 활용할 수 있습니다만, 모두 기억하기도 힘들고 찾아 보는 것도 만만치 않습니다. 따라서, 해당 내역들을 모두 보기 좋게 정리한 (https://ccbv.co.uk/projects/Django/2.2/) 를 이용합시다. (버전 주의!)

 

 

🚀 class based views

 

class based views를 사용해서 view를 작성하되, ListView를 상속받아 사용할 예정입니다.

view 모델을 정의하는데 곧바로 def xxx(request): ... 로 작성하지 않고 클래스로 정의했습니다.

from django.views.generic import ListView, DetailView

class HomeView(ListView):

    model = models.Room

    paginate_by = 10
    paginate_orphans = 5
    ordering = "created"
    
    # object_list의 이름을 바꿉니다. 안 바꾸면 그냥 object_list 로 사용하면 됩니다.
    context_object_name = "rooms"

 

urlpatterns을 정의할 때 함수만 받으므로 해당 클래스에 .as_view()를 붙여줘야 한다.

urlpatterns = [
    path("", room_views.HomeView.as_view(), name="home"),
]

 

그런데 views.py를 보면, 무엇을 렌더링할 것인지에 대한 정의가 없습니다. 실제로, 렌더링을 돌려보면TemplateDoesNotExist : rooms/room_list.html 오류가 납니다.

이는 지정된 이름(여기서는 room_list.html)을 찾아서 렌더링하기 때문입니다.

 

class based views가 찾는 이름으로 템플릿의 파일 이름을 바꿔줍시다.

 

 

🚀 ListView에서의 pagination

 

If you’re using pagination, you can adapt the example template from the pagination docs. Change instances of contacts in that example template to page_obj.

 

ListView를 사용한 뷰에서는 자동으로 page_obj 라는 이름으로 Page class를 가집니다.

 

또한, While this view is executing, self.object_list will contain the list of objects (usually, but not necessarily a queryset) that the view is operating upon.

 

object_list라는 이름의 해당 뷰가 지정한 모델 (위에서는 model = models.Room 라고 지정해줬으니 Room 모델을 사용 중이군요)의 리스트를 가지고 있습니다.

 

 

object_list, page_obj를 이용해서 렌더링하는 템플릿을 다음과 같이 활용할 수 있습니다. 꿀같습니다 😍

{% block content %}
  {% for room in object_list%}
    <h2>{{room.name}} / ${{room.price}}</h2>
  {%endfor%} 

  <h5>
    {% if page_obj.has_previous %}
        <a href="/?page={{page_obj.previous_page_number}}">Previous</a>
    {% endif %}

    Page {{page_obj.number}} of {{page_obj.paginator.num_pages}}

    {% if page_obj.has_next %}
        <a href="/?page={{page_obj.next_page_number}}">Next</a>
    {% endif %}
  </h5>

  {% for page in page_obj.paginator.page_range %}
    <a href="/?page={{page}}">{{page}}</a>
  {% endfor %}
{% endblock content %}


 

 

🚀 class based view에서 템플릿으로 데이터 넘기기

 

 

템플릿에게 뭔가 context를 넘겨주고 싶을 때, 함수형에서는 그냥 render를 돌리면서 세번 째 인자로 주면 됩니다.

물론 클래스 기반 뷰도 get_context_data 함수를 활용해서 템플릿에 특정한 데이터를 넘겨줄 수 있습니다.

 

class HomeView(ListView):

    model = models.Room

    paginate_by = 10
    paginate_orphans = 5
    ordering = "created"
    context_object_name = "rooms"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        now = timezone.now()
        context["now"] = now
        return context

 

보시다시피, return context를 해줘야 템플릿에서 해당 context를 사용할 수 있으며, super()를 통해 기존의 context를 승계해줘야 합니다. class-based-view는 자동으로 컨텍스트에 object_list, page_obj를 추가해주기 때문입니다.

 

 

 

🚀 function based view VS class based view

 

뭘 쓰든 구현은 됩니다만, class based view가 추상화의 정도 때문에 기피되기도 합니다. 또, function based는 어떤 방식으로 데이터나 코드가 실행되는지 한 페이지에서 다 살펴볼 수 있으므로 더 programmtic하다는 이유로 선호되기도 합니다.

 

뭔가 하드코어한 처리가 필요하면 function based view를 사용하면 됩니다.

 

 

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체