Django

Chapter 5-2 📌 Django CBV - ListView로 블로그 목록 + 검색 + 페이지네이션 구현하기

Chansman 2025. 5. 8. 10:01

📌 Django CBV - ListView로 블로그 목록 + 검색 + 페이지네이션 구현하기


1️⃣ FBV와 분리된 CBV 구조 설계

기존의 views.py 와는 구분하기 위해 cb_views.py 를 새로 만들어 CBV 관련 로직을 작성합니다.

# blog/cb_views.py
from django.db.models import Q
from django.views.generic import ListView
from blog.models import Blog

class BlogListView(ListView):
    queryset = Blog.objects.all()
    template_name = 'blog_list.html'
    paginate_by = 10
    ordering = ('-created_at', )

    def get_queryset(self):
        queryset = super().get_queryset()
        q = self.request.GET.get('q')
        if q:
            queryset = queryset.filter(
                Q(title__icontains=q) |
                Q(content__icontains=q)
            )
        return queryset

2️⃣ CBV URL 연결하기

# config/urls.py
from blog import cb_views

urlpatterns = [
    path('cb/', cb_views.BlogListView.as_view(), name='cb_blog_list'),
]

✔️ as_view() 메서드를 호출하여 클래스 기반 뷰 인스턴스를 생성합니다.


3️⃣ 템플릿에서 object_list 활용하기

CBV에서 queryset이 렌더링될 때 기본적으로 object_list라는 이름으로 템플릿에 전달됩니다.

{# blog/blog_list.html #}
{% extends 'base.html' %}
{% block content %}
    <h1>블로그 목록</h1>
    <p style="text-align: right">
        <a href="{% url 'blog_create' %}">생성</a>
    </p>

    {% for blog in object_list %}
        <p>
            <a href="{% url 'blog_detail' blog.pk %}">
                ({{ blog.id }}){{ blog.title }} - <small>{{ blog.created_at|date:"Y-m-d" }}</small>
            </a>
        </p>
    {% endfor %}

    <form method="get" style="margin-bottom: 10px;">
        <input name="q" type="text" placeholder="검색어를 입력하세요." value="{{ request.GET.q }}">
        <button>검색</button>
    </form>

4️⃣ 기존 FBV에서도 동일한 변수명 적용

FBV에서도 object_list와 page_obj를 전달하도록 수정합니다.

# blog/views.py
def blog_list(request):
    ...
    context = {
        'object_list': page_object.object_list,
        'page_obj': page_object,
    }
    return render(request, 'blog_list.html', context)

5️⃣ 페이지네이션 기능 추가 (CBV + FBV 공용)

{# blog_list.html 페이지네이션 영역 #}
<div>
    {% if page_obj.has_previous %}
        <a href="?page=1{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">&laquo 첫번째</a>
        <a href="?page={{ page_obj.previous_page_number }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">이전</a>
    {% endif %}

    {% if page_obj.number|add:2 > 1 %}
        <a href="?page={{ page_obj.number|add:-3 }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">&hellip;</a>
    {% endif %}

    {% for i in page_obj.paginator.page_range %}
        {% if page_obj.number == i %}
            <span>(현재페이지)</span>
        {% elif i > page_obj.number|add:-3 and i < page_obj.number|add:3 %}
            <a href="?page={{ i }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">{{ i }}</a>
        {% endif %}
    {% endfor %}

    {% if page_obj.paginator.num_pages > page_obj.number|add:2 %}
        <a href="?page={{ page_obj.number|add:3 }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">&hellip;</a>
    {% endif %}

    {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">다음</a>
        <a href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">마지막 &raquo</a>
    {% endif %}
</div>
{% endblock %}

✅ 정리

  • CBV는 ListView 상속을 통해 더 간결하고 구조화된 코드 작성이 가능합니다.
  • object_list, page_obj 등 변수명을 통일하면 FBV와 CBV가 같은 템플릿을 공유할 수 있습니다.
  • 검색 기능과 페이지네이션도 CBV에서 유연하게 구현할 수 있습니다.

💡 제너릭 뷰는 기본 동작이 많아 생산성이 매우 높습니다. 꼭 활용해보세요!