Djangoでページネーションを使う(django.core.paginator)

# 背景

- 1画面に表示する件数が多かったので、ページネーションを追加する。
- 標準の```django.core.paginator```を使う
- 環境は、Python 3.7.0 + Django 2.1.3


# 既存のコードの変更

## views.pyの変更

- ```from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger``` を追加

- ページネーション用の関数を追加

```python
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

# ページネーション用に、Pageオブジェクトを返す。
def paginate_query(request, queryset, count):
  paginator = Paginator(queryset, count)
  page = request.GET.get('page')
  try:
    page_obj = paginator.page(page)
  except PageNotAnInteger:
    page_obj = paginator.page(1)
  except EmptyPage:
    page_obj = paginatot.page(paginator.num_pages)
  return page_obj
```

- リクエストを受け取る関数の変更

```python
def myapp1(request):
  series = Series.objects.filter(deleted = False).order_by('-created_at')
  page_obj = paginate_query(request, series, settings.PAGE_PER_ITEM)   # ページネーション
  return render(request, 'app1/index.html', {'page_obj': page_obj, 'site_name':settings.SITE_NAME})  # モデルから取得したobjectsの代わりに、page_objを渡す
```

## テンプレートの変更

- モデルから取得したobjectsの代わりに、page_objを使って表示。

```html
        {% for s in series %}
                <p>{{ s.title }}</p>
        {% endfor %}
```

を、seriesをpage_objに置き換える(for文の中身はそのままでよい)

```html
        {% for s in page_obj %}
                <p>{{ s.title }}</p>
        {% endfor %}
```

- ページネーション部分を追加(別ファイルに作成して、includeするとよい)

```html
<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">next</a>
            <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>
```

## 設定ファイルに、1ページに表示する件数を指定

```
PAGE_PER_ITEM = 15
```


# Bootstrap4を使って見映えをよくする

- for文用のページ番号のリストを得るために、```page_obj.paginator.page_range```を使う
- 他は、```has_previous```, ```previous_page_number```, ```has_next```, ```next_page_number```, ```number```で指定する


```html
<div>
    <nav aria-label="Page navigation example">
        <ul class="pagination">
            {% if page_obj.has_previous %}
                <li><a class="page-link text-primary d-inline-block" href="?page={{ page_obj.previous_page_number }}">前</a></li>
            {% else %}
                <li class="disabled"><div class="page-link text-secondary d-inline-block disabled" href="#">前</div></li>
            {% endif %}

            {% for link_page in page_obj.paginator.page_range %}
                {% if link_page %}
                    {% if link_page == page_obj.number %}
                        <li class="disabled"><div class="page-link text-secondary d-inline-block disabled" href="#">{{ link_page }}</div></li>
                    {% else %}
                        <li><a class="page-link text-primary d-inline-block" href="?page={{ link_page }}">{{ link_page }}</a></li>
                    {% endif %}
                {% else %}
                    <li class="disabled"><a class="page-link text-secondary d-inline-block text-muted" href="#">・・・</a></li>
                {% endif %}
            {% endfor %}
    
            {% if page_obj.has_next %}
                <li><a class="page-link text-primary d-inline-block" href="?page={{ page_obj.next_page_number }}">次</a></li>
            {% else %}
                <li class="disabled"><div class="page-link text-secondary d-inline-block disabled" href="#">次</div></li>
            {% endif %}
        </ul>
    </nav>
</div>
```
- このような見映えになります。

![スクリーンショット 2018-12-27 15.05.47.png](https://qiita-image-store.s3.amazonaws.com/0/142577/dcf8df94-3532-4cce-0584-8289c0f38716.png)



# 参考ページ

- 公式ドキュメントの[ページネーション - Page オブジェクト](https://docs.djangoproject.com/ja/2.1/topics/pagination/#page-objects)
- [Django、ページング処理まとめ](https://narito.ninja/detail/89/)
- [DjangoでPaginationを実装する](https://qiita.com/t-iguchi/items/5f276f44d9e3bea76e1e)
- Bootstrap4の公式ページの[Pagination](https://getbootstrap.com/docs/4.0/components/pagination/)