본문 바로가기

개발이나하자../backend

[django] django REST framework 페이징 처리 + angular ngx-pagination

djando REST framework Pagination

django Rest Freamwork에서 pagination을 처리하는 방법은 여러 가지가 있다. 

https://www.django-rest-framework.org/api-guide/pagination/

 

Pagination - Django REST framework

pagination.py Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links. — Django documentation REST framework includes support for customizable pagination styles. This a

www.django-rest-framework.org

여기 참고 하길 .. 근데 나는 읽어도 무슨 소린지 한참 봐야 했다. 개발은 방법이 너무 다양해서 그 많은 방법 중에 하나를 고르는 게 어려운 듯.. ㅎㅎ 아무튼,.

 

 

먼저 settings.py에 아래 부분을 추가한다. LimitOffsetPagination을 사용하려고 했는데 페이지 번호를 지정하는 법을 몰라서 PageNumberPagination을 사용했다.

eg) 두번째 페이지 주세요. => LimitOffsetPagination을 이용해서 어떻게 하는지 모르겠어서


# settings.py

REST_FRAMEWORK = {

    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination'
    # 나는 LimitOffsetPagination 대신 PageNumberPagination을 사용했다.
    
}

 

 

settings.py 에 `PAGE_SIZE` 를 global로 정의할 수 도 있지만 customising 하고 싶다면 따로 클래스를 만들면 된다.

나는 대시보드에서는 리스트 사이즈가 5개, 그냥 일반 리스트에서는 사이즈를 20개씩 불러오기 때문에 이렇게 클라스를 만들어봤다.


# pagination.py


class DashboardPageNumberPagination(PageNumberPagination):
	page_size = 5
    # 여기서 다른 값들도 설정 가능.. 난 일단 사이즈만 정하겠음


class ListPageNumberPagination(PageNumberPagination):
	page_size = 20

 

 

 

 

url : /api/lang/categories?page_size=5 위 url은 아래 view를 부른다.dddd/categories/?page_size=5/api/lang/categories/?page_size=


# view.py


@api_view(['GET'])
def get_categories(request):
    try:

        page_size = 20
        if 'page_size' in request.query_params:
            page_size = request.query_params['page_size']

        categories = Category.objects.filter()

        # paging
        if int(page_size) == 5:
            paginator = DashboardPageNumberPagination() # 여기서 아까 만들었던 클라스를 사용한다.
        else:
            paginator = ListPageNumberPagination() # 리스트니깐 20개씩 불러온다.

        context = paginator.paginate_queryset(categories, request) # 불러온 데이터를 paginator.paginate_queryset 하니깐 page_size 만큼 만 보여진다

        paging = get_pagination_result(paginator, categories.count()) # 내가 만든 페이지 객체

        serializer = CategorySerializer(context, many=True) # serialize 한다

        res = {
            'page': paging,
            'data': serializer.data
        }

        return JsonResponse(res, safe=False) # response 반환
    except Category.DoesNotExist:
        return HttpResponse(status=404)
	

 

 


# pagination.py

def get_pagination_result(paginator, total_items):
	items_per_page = paginator.page_size
    current_page = paginator.page.number
    
    return {
    	'items_per_page': items_per_page,  # 한 페이지에 보여지는 갯수 (나는 20 or 5)
        'current_page': current_page,      # 현재 페이지 (default 1)
        'total_items': total_items		   # 총 아이템 개수
    }

 

 

url : /api/lang/categories? page_size=5에 대한 Response는 이렇게 받는다.

두 번째 페이지를 불러오고 싶다면 url 은?

url : /api/lang/categories? page_size=5&page=2를 사용한다.  여기서 page라는 값을 사용해야

context = paginator.paginate_queryset(categories, request)  - paginate_queryset에서 page라는 값을 default로 사용하게 되어있다.

바꾸는 방법은 있겠지만 난 그냥 있는 대로 쓰겠어요 ^_^

Response는 이렇게 온다. page 부분은 내가 만든 것. UI에서 저렇게 받으면 편하기 때문에.. 

 

서버는 이렇게 처리했음.. 그럼 UI에서는?

 

 

angular에서 ngx-pagination 이용해서 paging 처리하기

paging component는 ngx-pagination을 사용했다.

https://github.com/michaelbromley/ngx-pagination#readme

 

michaelbromley/ngx-pagination

Pagination for Angular. Contribute to michaelbromley/ngx-pagination development by creating an account on GitHub.

github.com

 

먼저 설치부터 한다, 

npm install ngx-pagination --save

 

사용하는 모듈에서 import를 해준다. 나는 languageModule에서 사용하겠다. imports 안에 불러온다.

 

서버에서 보낸 response를 보면 아래 html을 이해하기 쉬울 것이다.

response 안에 data와 page 가 있다. data 안에는 category list 가 있고 page 안에는 paging 정보가 들어있다.


language.html

<ul class="list-group list-group-flush"  *ngFor="let i of categories['data'] | paginate:
    { id: 'category',
      itemsPerPage: categories['page']['items_per_page'],
      currentPage: categories['page']['current_page'],
      totalItems: categories['page']['total_items']
     }"
>
<li class="list-group-item"><a href="javascript:" (click)="onClickCategory(i.id)">{{i.name}}</a></li>
</ul>

<pagination-controls id="category" (pageChange)="pageChanged($event)"></pagination-controls>

<!-- 1. ul에서 list를 for문으로 돌 때 paginate directive 를 사용한다.
	 2. directive 안에 id 를 고유한 값으로 정한다. 나는 category 를 사용했다.
     3. itemsPerPage : 한 페이지에 몇 개의 아이템을 보여줄 것 인가.
     4. currentPage: 현재 페이지는 몇번인가
     5. totalItems: 총 아이템은 몇개인가
     6. <pagination-controls> 부분이 페이징 컨트롤 html 부분이다
     7. paginate directive 에 지정한 id를 <pagination-controls> 여기 id로 지정한다.
     8. (pageChange)는 1,2,3, ... 을 클릭했을 때 페이지를 이동할 때 여기를 탄다
-->

 

 

위에 부분이 이 페이징 컨트롤을 말한다.

 

 


 

 


// language.component.ts


// category list 불러오는 메소드
getCategories(page: number = 1, search_text?:string) {
    this.langService.getCategories(this.page_size, page, search_text).subscribe((result) => {
      this.categories = result;
    })
}


// next or prev 페이지를 불러오는 함수 - data 는 숫자이다.
// 예) 페이징 컨트롤에서 1을 클릭하면 1, 4페이지를 클릭하면 4를 말한다.
pageChanged(data) {
    this.getCategories(data);
}

 

 

url : api/lang/categories?page_size=5

 

 

url : api/lang/categories?page_size=5&page=2

 

이렇게 페이징 처리를 완료했다. 참 어렵구나 ^_^