▶️ Django 

[Django] 페이지네이션(pagination)

 

메인 페이지에는 총 6개의 게시물만 보이게 할 거예요.

6개가 넘어간다면 페이지 넘기는 pagination을 만들어볼게요.


변경 전, 메인 페이지

게시물이 6개가 넘었는데도 밑으로 계속 생성되고 있어요.
Django에서 제공해주는 페이지 네이게이션 함수를 써서 구현해 볼게요.

Django에서 제공하는 페이지 네비게이션을 사용할 거예요.

총 3가지 방법이 있지만, 일반적으로 사용되는 "Paginator"으로 구현할게요.

 

구현 시작

articles/views.py

# 변경 전 views.py 코드
class ArticleList(View):
    def get(self, request):
        try:
            article_list = Article.objects.all()
            
            if not article_list.exists():
                article_list = None
                
        except DatabaseError as db_err:
            print(f"Database error occurred: {db_err}")
            article_list = None  # 데이터베이스 오류 시 None 설정
            print(f"Unexpected error occurred: {e}")
            article_list = None  # 기타 예외 발생 시 None 설정

        context = {"article_list": article_list}
        return render(request, "main.html", context)
현재, 게시물을 보여주는 코드는 이렇게 구현돼 있어요.
이제 여기에 Paginator 설정 관련 코드를 추가해 줄게요.

구현하며 참고한 블로그 : https://niceit.tistory.com/401

 

수정한 articles/views.py

# 게시글 목록 보기
class ArticleList(View):
    def get(self, request):
        try:
            article_list = Article.objects.all().order_by("-id") # 250325 수정
        
            if not article_list.exists():
                article_list = None
            
            page = request.GET.get("page", 1) # URL에서 현재 페이지 번호를 가져옴 (기본값: 1) 250325 추가
            paginator = Paginator(article_list, 6) # 페이지당 6개의 게시물로 페이지네이터 생성 250325 추가
            
            try: # 예외 처리 추가
                articles = paginator.get_page(page)  # 요청된 페이지에 해당하는 게시물 가져오기 250325 추가
            except PageNotAnInteger: # 페이지 번호가 정수가 아닌 경우 250325 추가
                articles = paginator.get_page(1) # 1페이지를 보여준다 250325 추가
            except EmptyPage: # 페이지가 비어있는 경우 250325 추가
                articles = paginator.get_page(paginator.num_pages) # 마지막 페이지를 보여준다 250325 추가
        
            is_paginated = articles.has_other_pages()  # 페이지네이션이 필요한지 확인 250325 추가
            
            
        except DatabaseError as db_err:
            print(f"Database error occurred: {db_err}")
            article_list = None  # 데이터베이스 오류 시 None 설정

        except Exception as e:
            print(f"Unexpected error occurred: {e}")
            article_list = None  # 기타 예외 발생 시 None 설정

        context = {
            "article_list": article_list,
            "articles": articles,  # 현재 페이지의 게시물 250325 추가
            "is_paginated": is_paginated,  # 페이지네이션 UI를 표시할지 여부 250325 추가
            }
        return render(request, "main.html", context)
추가한 코드는
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage  # 250325 추가​

# .order_by("-id") 추가
article_list = Article.objects.all().order_by("-id") # 250325 수정​
page = request.GET.get("page", 1) # URL에서 현재 페이지 번호를 가져옴 (기본값: 1) 250325 추가
paginator = Paginator(article_list, 6) # 페이지당 6개의 게시물로 페이지네이터 생성 250325 추가
            
try: # 예외 처리 추가
    articles = paginator.get_page(page)  # 요청된 페이지에 해당하는 게시물 가져오기 250325 추가
except PageNotAnInteger: # 페이지 번호가 정수가 아닌 경우 250325 추가
    articles = paginator.get_page(1) # 1페이지를 보여준다 250325 추가
except EmptyPage: # 페이지가 비어있는 경우 250325 추가
    articles = paginator.get_page(paginator.num_pages) # 마지막 페이지를 보여준다 250325 추가
        
is_paginated = articles.has_other_pages()  # 페이지네이션이 필요한지 확인 250325 추가​
context = {
            "article_list": article_list,
            "articles": articles,  # 현재 페이지의 게시물 250325 추가
            "is_paginated": is_paginated,  # 페이지네이션 UI를 표시할지 여부 250325 추가
            }​

이렇게 추가하였어요!

자세한 설명은 주석을 참고해 주세요.

 

articles/templates/main.html

▼ 추가한 HTML 내용

더보기
<!-- Pagination 250325 추가 -->
{% if is_paginated %} <!-- 페이지네이션이 필요한 경우 실행 -->
<nav aria-label="Page navigation">
    <ul class="pagination justify-content-center"> <!-- 부트스트랩 pagination 클래스 적용, 가운데 정렬 -->

        {% if articles.has_previous %} <!-- 이전 페이지가 있는 경우 -->
            <li class="page-item">
                <!-- 이전 페이지 링크, articles:main url 사용, 이전 페이지 번호 전달, 접근성 aria-label -->
                <a class="page-link" href="{% url "articles:main" %}?page={{ articles.previous_page_number }}" aria-label="Previous">
                    <span aria-hidden="true">«</span> <!-- 이전 페이지 화살표, 스크린 리더에서 숨김 -->
                </a>
            </li>
            {% else %} <!-- 이전 페이지가 없는 경우 -->
            <li class="page-item disabled">
                <a class="page-link">«</a>
            </li>
        {% endif %}

        {% for i in articles.paginator.page_range %} <!-- 페이지 범위 순회 -->
        {% if articles.number == i %} <!-- 현재 페이지인 경우 -->
        <li class="page-item active" aria-current="page"> <!-- aria-current로 현재 페이지 표시 -->
            <a class="page-link" href="#">{{ i }}</a> <!-- 현재 페이지 번호 (링크 없음) -->
        </li>

        {% else %} <!-- 현재 페이지가 아닌 경우 -->
        <!-- 다른 페이지로 이동하는 링크, 페이지 번호 전달 -->
        <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
        {% endif %}
        {% endfor %}

        {% if articles.has_next %} <!-- 다음 페이지가 있는 경우 -->
            <li class="page-item" aria-current="page"> <!-- aria-current로 현재 페이지 표시 -->
                <!-- 다음 페이지 링크, articles:main url 사용, 다음 페이지 번호 전달, 접근성 aria-label -->
                <a class="page-link" href="{% url "articles:main" %}?page={{ articles.next_page_number }}" aria-label="Next">
                    <span aria-hidden="true">»</span> <!-- 다음 페이지 화살표, 스크린 리더에서 숨김 -->
                </a>
            </li>
        {% else %} <!-- 다음 페이지가 없는 경우 -->
        <li class="page-item disabled">
            <a class="page-link">»</a>
        </li>
        {% endif %}
    </ul>
</nav>
{% endif %}
<!-- Pagination End 250325 추가 -->

▼ 전체 HTML

더보기
{% extends "base.html" %}
{% load static %}

{% block content %}
<div class="wrapper">
    <div class="container-fluid">
        <div class="row">
            <!-- Left Buttons -->
            <div class="col-md-3">
                <button class="btn-custom w-100" onclick="location.href='/workspace/'">
                    Create to text ✏️ <i class="fas fa-file"></i>
                </button>
                <button class="btn-custom w-100">
                    Create to image 📷 <i class="fas fa-image"></i>
                </button>
                <button class="btn-custom w-100" onclick="location.href='{% url 'prompts:generate-prompt-api' %}'">
                    Create to 3D Model Prompt 🤖
                </button>
            </div>
    
            <!-- Image Gallery -->
            <div class="col-md-9">
                <div class="row">
                    {% if articles %} <!--변수명 수정, "article_list -> articles" 250325-->
                        {% for article in articles %} <!--변수명 수정, "article_list -> articles" 250325-->
                        <div class="col-md-4">
                            <div class="main-card">
                                <a style="color: inherit; text-decoration: none;" href="{% url 'articles:articledetail' article.pk %}">
                                    <!-- Image Handling -->
                                    <div class="main-card-img-top">
                                        {% if article.image_path %}
                                            {% if article.image_path.name|slice:":4" == "http" %}
                                                <img src="{{ article.image_path.name }}" class="preview-image" alt="Preview Image">
                                            {% else %}
                                                <img src="{{ article.image_path.url }}" class="preview-image" alt="Preview Image">
                                            {% endif %}
                                        {% else %}
                                            <div class="placeholder-image">
                                                <i class="fas fa-image"></i>
                                            </div>
                                        {% endif %}
                                    </div>
                                    
                                    <!-- Like/Dislike -->
                                    <div class="interaction-area">
                                        <form method="post" action="{% url "articles:articlelike" article.pk "❤️" %}" class="inline-form">
                                            {% csrf_token %}
                                            <button type="submit" class="btn btn-like">❤️</button>
                                            <span class="like-count">{{ article.like_count }}</span>
                                        </form>
                                        <form method="post" action="{% url "articles:articlelike" article.pk "🤨" %}" class="inline-form">
                                            {% csrf_token %}
                                            <button type="submit" class="btn btn-dislike">🤨</button>
                                            <span class="dislike-count">{{ article.dislike_count }}</span>
                                        </form>
                                    </div>

                                    <!-- Meta Info -->
                                    <div class="card-body">
                                        <p class="card-content">
                                            <strong>User ID:</strong> {{ article.user_id }}<br>
                                            <strong>Job ID:</strong> {{ article.job_id }}
                                        </p>
                                    </div>
                                </a>
                            </div>
                        </div>
                        {% endfor %}
                    {% else %}
                        <div class="col-md-12">
                            <p class="no-articles">No articles available.</p>
                        </div>
                    {% endif %}
                </div>

                <!-- Pagination 250325 추가 -->
                {% if is_paginated %} <!-- 페이지네이션이 필요한 경우 실행 -->
                <nav aria-label="Page navigation">
                    <ul class="pagination justify-content-center"> <!-- 부트스트랩 pagination 클래스 적용, 가운데 정렬 -->

                        {% if articles.has_previous %} <!-- 이전 페이지가 있는 경우 -->
                            <li class="page-item">
                                <!-- 이전 페이지 링크, articles:main url 사용, 이전 페이지 번호 전달, 접근성 aria-label -->
                                <a class="page-link" href="{% url "articles:main" %}?page={{ articles.previous_page_number }}" aria-label="Previous">
                                    <span aria-hidden="true">«</span> <!-- 이전 페이지 화살표, 스크린 리더에서 숨김 -->
                                </a>
                            </li>
                            {% else %} <!-- 이전 페이지가 없는 경우 -->
                            <li class="page-item disabled">
                                <a class="page-link">«</a>
                            </li>
                        {% endif %}

                        {% for i in articles.paginator.page_range %} <!-- 페이지 범위 순회 -->
                        {% if articles.number == i %} <!-- 현재 페이지인 경우 -->
                        <li class="page-item active" aria-current="page"> <!-- aria-current로 현재 페이지 표시 -->
                            <a class="page-link" href="#">{{ i }}</a> <!-- 현재 페이지 번호 (링크 없음) -->
                        </li>

                        {% else %} <!-- 현재 페이지가 아닌 경우 -->
                        <!-- 다른 페이지로 이동하는 링크, 페이지 번호 전달 -->
                        <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
                        {% endif %}
                        {% endfor %}

                        {% if articles.has_next %} <!-- 다음 페이지가 있는 경우 -->
                            <li class="page-item" aria-current="page"> <!-- aria-current로 현재 페이지 표시 -->
                                <!-- 다음 페이지 링크, articles:main url 사용, 다음 페이지 번호 전달, 접근성 aria-label -->
                                <a class="page-link" href="{% url "articles:main" %}?page={{ articles.next_page_number }}" aria-label="Next">
                                    <span aria-hidden="true">»</span> <!-- 다음 페이지 화살표, 스크린 리더에서 숨김 -->
                                </a>
                            </li>
                            {% else %} <!-- 다음 페이지가 없는 경우 -->
                            <li class="page-item disabled">
                                <a class="page-link">»</a>
                              </li>
                        {% endif %}
                    </ul>
                </nav>
                {% endif %}
                <!-- Pagination End 250325 추가 -->
            </div>
        </div>
    </div>
</div>

<!-- CSS -->
<style>
    /* Container Styling */
    .container {
        margin-top: 20px;
    }

    /* Card Styling */
    .main-card {
        border: none;
        border-radius: 12px;
        overflow: hidden;
        background-color: #ffffff;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        transition: transform 0.2s ease, box-shadow 0.2s ease;
        margin-bottom: 20px;
        position: relative;
    }

    .main-card:hover {
        transform: translateY(-5px);
        box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
    }

    /* Image Handling */
    .main-card-img-top {
        width: 100%;
        height: 200px;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: #f0f0f0;
        overflow: hidden;
        border-top-left-radius: 12px;
        border-top-right-radius: 12px;
    }

    .preview-image {
        width: 100%;
        height: 100%;
        object-fit: cover;
        transition: transform 0.3s ease;
    }

    .preview-image:hover {
        transform: scale(1.05);
    }

    .placeholder-image {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #aaa;
        font-size: 36px;
        background-color: #e0e0e0;
        border-radius: 12px;
    }

    /* Card Body Styling */
    .card-body {
        padding: 16px;
    }

    .card-content {
        font-size: 14px;
        color: #333;
        margin-bottom: 8px;
    }

    /* Interaction Area */
    .interaction-area {
        display: flex;
        justify-content: space-between;
        padding: 8px 16px;
        background-color: #fafafa;
        border-top: 1px solid #eee;
    }

    .inline-form {
        display: flex;
        align-items: center;
        margin-right: 8px;
    }

    .btn-like,
    .btn-dislike {
        background-color: transparent;
        border: none;
        font-size: 18px;
        cursor: pointer;
        transition: transform 0.2s ease;
    }

    .btn-like:hover,
    .btn-dislike:hover {
        transform: scale(1.2);
    }

    .like-count,
    .dislike-count {
        font-size: 14px;
        margin-left: 4px;
        color: #555;
    }

    /* Left Button Styling */
    .btn-custom {
        background-color: #f8f9fa;
        color: #333;
        border: 1px solid #ced4da;
        border-radius: 6px;
        padding: 12px;
        margin-top: 12px;
        width: 100%;
        font-size: 16px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        transition: background-color 0.2s ease;
    }

    .btn-custom:hover {
        background-color: #e9ecef;
    }

    .btn-custom i {
        margin-left: 8px;
    }

    /* No Articles Message */
    .no-articles {
        text-align: center;
        font-size: 18px;
        color: #888;
        margin-top: 20px;
    }

    /* Responsive Design */
    @media (max-width: 768px) {
        .main-card-img-top {
            height: 160px;
        }

        .btn-custom {
            font-size: 14px;
        }

        .card-content {
            font-size: 12px;
        }
    }
</style>
<script src="{% static 'prompt_js/prompt.js' %}"></script>
{% endblock %}

🔙 ▼ HTML


변경 후, 메인 페이지 

굿!

색상은 검은색으로 변경할 예정이에요.

 

+ Recent posts