Django | class형 view

Jihun Kim·2021년 10월 21일
0

Django

목록 보기
3/8
post-thumbnail
post-custom-banner

Django의 class형 view

Django에서는 함수형 view와 함께 class형 view 역시 지원한다.
이는 많이 쓰이는 것을 클래스로 만들어 두어 사용의 편의성을 높이기 위해 만들어 졌다.

함수형 view를 class형 view로 만들기

module import

from django.views import View

views.py

class PostCreateView(View):
	def get(self, request):
    	    post_form = PostForm()
            return render(request, 'posts/post_form.html', {'form': post_form}
        def post(self, request):
    	    post_form = PostForm(request.POST)
            if post_form.is_valid():
        	new_post = post_form.save()
            return redirect('post-detail', post_id=new_post.id)

urls.py
클래형 뷰는 함수형 뷰와 쓰는 방식이 조금 다름
.as_view()를 추가해 주어야 함

from django.urls import path, include
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('posts/', views.PostListView.as_view(), name='post-list'),
    ...
]

Generic view

장고 개발자들이 자주 쓸만한 형태의 view를 하나의 형태로 만들어 둔 것
자주 사용하는 기능이 구현되어 있어, 제네릭 뷰를 상속하면 원하는 기능을 빠르게 구현할 수 있다.

  • model, template만 설정해 주면 된다.
  • context를 따로 적어주지 않아도 된다.
  • reverse는 url name으로부터 거슬러 올라가 url을 찾는다는 의미로 생각하면 됨

CreateView

from django.views.generic import CreateView
from django.urls import reverse

class PostCreateView(CreateView):
    model = Post  # 우리가 사용할 모델 할당
    form_class = PostForm  # 우리가 사용할 폼
    template_name = 'posts/post_form.html'  # 렌더할 템플릿

	# 이동할 url
    def get_success_url(self):
        # kwargs = keyword argument의 약자로, 사전형으로 키워드를 이용해서 값을 전달시 사용
        # self.objects.id를 통해 새로 생성된 post 데이터 객체에 접근할 수 있다.
        # reverse(<이동할 url>, <context>)
        return reverse('post-detail', kwargs={'post_id': self.object.id})

ListView

  • 넘겨줄 데이터 설정 및 페이지네이션까지 가능하다.
class PostListView(ListView):
    model = Post
    template_name = 'posts/post_list.html'
    context_object_name = 'posts'  # 넘겨줄 데이터의 이름
    # 글을 최신 순으로 정렬 -> '-' 기호를 붙이면 가장 최신 순으로 정렬됨
    # '-' 기호를 붙이지 않으면 오름차순으로 정렬됨
    ordering = ['-dt_created']
    # 몇 개 단위로 페이지를 나눌 것인지 결정
    paginate_by = 6
    # 현재 페이지를 쿼리 스트링의 어떤 값으로 조회하는지 알려주게 됨
    page_kwarg = 'page'
  • generic list view는 page를 'page'가 아닌 'page_obj'로 전달한다. 따라서 이에 맞게 template 역시 변경해 주어야 한다.
{% if page_obj.object_list %}
    <div class="post_container">
    {% for post in page_obj.object_list %}
        <div class="post">
            <a href={% url 'post-detail' post.id %}>
                <h2 class="title">{{post.title}}</h2>
                <p class="date">{{post.dt_created}}</p>
                <p class="text">{{post.content|slice:":100"}}</p>
            </a>
        </div>
    {% endfor %}
    </div>
    <div class="paginator">
        {% if page_obj.has_previous %}
            <a href="?page=1" class="first">first</a>
            <a href="?page={{ page_obj.previous_number }}" class="prev">prev</a>
        {% endif %}
        <span>
            <p>{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</p>
        </span>
        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}" class="next">next</a>
            <a href="?page={{ page_obj.paginator.num_pages }}" class="last">last</a>
        {% endif %}
    </div>
{% else %}
  • urls.py에서도 class형 view로 인식하도록 변경해 준다.
urlpatterns = [
    path('posts/', views.PostListView.as_view(), name='post-list'),
]

DetailView

  • views.py
from django.views.generic import DetailView

class PostDetailView(DetailView):
    model = Post
    template_name = 'posts/post_detail.html'
    # 특정 id에 해당하는 post를 보여줘야 함
    pk_url_kwarg = 'post_id'
    context_object_name = 'post'
  • urls.py
urlpatterns = [
    path('posts/<int:post_id>', views.PostDetailView.as_view(), name='post-detail'),
]

UpdateView

  • views.py
from django.views.generic import UpdateView

class PostUpdateView(UpdateView):
    model = Post
    form_class = PostForm()
    template_name = "posts/post_form.html"
    pk_url_kwarg = 'post_id'

    # postform의 유효성 검증 통과시 이동할 url
    def get_success_url(self):
        return reverse('post-detail', kwargs={'post_id': self.object.id})
  • urls.py
urlpatterns = [
    path('posts/<int:post_id>/edit',
         views.PostUpdateView.as_view(), name='post-update')
]

DeleteView

  • views.py
from django.views.generic import DeleteView

class PostDeleteView(DeleteView):
    model = Post
    template_name = "posts/post_confirm_delete.html"
    pk_url_kwarg = 'post_id'
    context_object_name = 'post'

    def get_success_url(self):
        return reverse('post-list')
  • urls.py
urlpatterns = [
    path('posts/<int:post_id>/delete',
         views.PostDeleteView.as_view(), name='post-delete'),
]

Generic view의 context

ListView

  • ListView의 경우 목록을 보여 주어야 하기 때문에 데이터베이스에서 조회한 모든 데이터가 'object_list'와 '<모델명>_list'라는 두 개의 키로 template으로 전달된다.
    👉 여기서는 'post_list'가 전달 된다.

DetailView

  • 하나의 데이터를 전달하는데, 이 때 '<모델명>'으로 전달한다.
    👉 여기서는 'post'가 전달 된다.
    👉 UpdateView, DeleteView 역시 이와 마찬가지이다.

'context_object_name'은 view에서 template으로 전달하는 모델 데이터에 대한 이름을 저장해 준다.
👉 그런데 이를 따로 지정하지 않으면 모델명을 이용해 template으로 '<모델명>_list'로 전달하게 된다.

  • 만약 'context_object_name'을 아래와 같이 따로 지정해 주면 '<모델명>_data'로 자동으로 전달된다.
context_object_name = 'model_data'
  • 모델 데이터 외에도 다양한 데이터를 template으로 전달할 수 있다.
    👉 template으로 전달하는 context 내용은 다음과 같다.
    👉 만약 is_paginated = True라면 '현재 페이지'에 해당하는 데이터만 전달 된다.
{
	'paginator': <Django에서 제공하는 Paginator 객체>  # paginate_by를 설정해 주지 않으면 default는 None이다.
    'page_obj': <Page 객체>,
    'is_paginated': False,
    'object_list': [데이터 목록이 담긴 QuerySet],
    '<모델명>_list': [데이터 목록이 담긴 QuerySet]
}

Generic View의 default 값 이용해 간결하게 작성하기

ListView

  • template_name: '<모델명>_list.html'
    👉 만약 templates에 있는 template과 이름이 일치한다면 굳이 적지 않아도 된다.
  • context_object_name: model 명에 맞게 'post_list'로 전달 됨
    👉 따라서, 만약 pagination이 되어 있다면 object_list에는 pagination 된 list가 전달되기 때문에 template에서 아래와 같이 명시하지 않아도 된다.

[기존]

{% if page_obj.object_list %}
    <div class="post_container">
    {% for post in page_obj.object_list %}
        <div class="post">
            <a href={% url 'post-detail' post.id %}>
			...

[바뀐 후]

{% if page_obj.object_list %}
    <div class="post_container">
    {% for post in object_list %} // page_obj는 지워도 된다.
        <div class="post">
            <a href={% url 'post-detail' post.id %}>
			...
  • page_kwarg: 이미 template에서 key를 'page'로 이용하고 있기 떄문에 따로 적어주지 않아도 ?page=의 형식으로 쿼리 스트링이 전달 되기 때문에 굳이 적지 않아도 된다.

DetailView

  • template_name의 기본 값은 ListView에서와 마찬가지로 '<모델명>_list.html'이기 때문에 적지 않아도 된다.
  • pk_url_kwarg: 기본 값이 'pk'이다.
    👉 따라서 이를 이용하기 위해 urls.py에서 'post_id'가 아니라 'pk'로 변경해 주고 해당 부분은 지워도 된다.
  • context_object_name: object라는 키워드와 모델명('Post')을 소문자로 적은 'post'가 기본적으로 사용되기 때문에 해당 부분은 지워도 된다.

아래와 같이 적기만 하면 된다.

class PostDetailView(DetailView):
    model = Post

CreateView

  • form_class: 자동으로 template에 'form'이라는 이름으로 키워드가 전달된다.
  • template_name: 기본 키워드명이 'post_form.html'이므로 삭제해도 된다.
  • def get_success_url: kwargs={'post_id'...} -> kwargs={'pk'...}으로 변경해 준다.
class PostCreateView(CreateView):
    model = Post  # 우리가 사용할 모델 할당
    form_class = PostForm  # 우리가 사용할 폼

    def get_success_url(self):
        # 이동할 url
        # kwargs = keyword argument의 약자로, 사전형으로 키워드를 이용해서 값을 전달시 사용
        # self.objects.id를 통해 새로 생성된 post 데이터 객체에 접근할 수 있다.
        return reverse('post-detail', kwargs={'pk': self.object.id})

나머지 update, delete도 유사하게 간결하게 수정할 수 있다.

⚡ DeleteView의 경우

  • template_name의 default 값은 'post_confirm_delete.html'이다.
    👉 삭제의 경우 한 번 확인하고 진행한다는 가정이 되어 있어 그렇다!

IndexView

  • index의 경우 함수형 뷰와 클래스형 뷰의 코드 차이가 크지 않다.
  • 오히려 함수형 뷰가 더 직관적으로 보이기 때문에 이럴 때는 함수형 뷰를 써도 괜찮다.
from django.views.generic import RedirectView
# 함수형 뷰
def index(request):
    return redirect('post-list')

# 클래스형 뷰
class IndexRedirectView(RedirectView):
    pattern_name = 'post-list'


정리

  • CRUD를 사용하는 경우 generic이 간단하기 때문에 사용하는 것이 좋다!
profile
쿄쿄
post-custom-banner

0개의 댓글