Djangoのページネーション実装でページあたりのコンテンツ数を変更できるように

# はじめに
Djangoでページネーションの実装を行う必要があった。
その際、ページあたりのコンテンツの表示数を変更できるような実装を行いたかった。
今回は記事の一覧表示を行うアプリケーションを参考に実装方法を示す。

## 環境
- Python 3.7.3
- Django 2.1.6
- bootstrap4

## Model
```python:app/models.py
from django.db import models

class Article(models.Model):
  title = models.CharField('title', max_length=255)
  content = models.CharField('content', max_length=255)
```

## Form
コンテンツの表示数を10〜50で変更できるようにしている。
また、今回はセレクトボックスで値を選択した時点で、`submit`も行いたいため、
`widget=forms.Select(attrs={'onchange': 'submit(this.form);`})
のようにしている。

```python:app/forms.py
from django import forms

PAGINATE_BY_CHOICES = (
  ('', '-'*10),
  (10, '10'),
  (20, '20'),
  (30, '30'),
  (40, '40'),
  (50, '50')
)

class Paginate(forms.Form):
  paginate_by = forms.ChoiceField(
    label='記事表示数',
    widget=forms.Select(attrs={'onchange': 'submit(this.form);'}),
    choices=PAGENATE_BY_CHOICES
  )
```

## View
Viewは`django.view.View`を利用している。現在Djangoを勉強中のため、むやみに汎用ビューを使いたくないという理由からです。
デフォルトではページあたり10個の記事を表示します。
また、セッションに選択したページあたりのコンテンツ数を保存することで、ページ遷移しても大丈夫なようにしています。

```python:app/views.py
from django.views import View
from .models import Article
from . import forms
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage

class ArticleList(View):
  paginate_by = 10

  def get(self, request, **kwargs):
    form = forms.Paginate(request.GET or None)

    if 'paginate_by' in request.GET:
      request.session['paginate_by'] = request.GET['paginate_by']

    if 'paginate_by' in request.session:
      self.paginate_by = request.session['paginate_by']

    queryset = Article.objects.all()
    paginator = Paginator(queryset, self.paginate_by)
    try:
      contents = paginator.page(kwargs['page'])
    except PageNotAnInteger:
      contents = paginator.page(1)
    except EmptyPage:
      contents = paginator.page(paginator.num_pages)
    context = {}
    context['contents'] = contents
    context['form'] = form

    return render(request, 'app/article_list.html', context)
```

## URLs
```python:app/urls.py
from django.urls import path
from . import views

app_name = 'app'

urlpatterns = [
  path('article_list/<int:page>', views.ArticleList.as_view(), name='article_list'),
]
```

## Template
```python:app/aritcle_list.html
  <form name="paginate_form" action="{% url 'main:user_list' 1 %}" method="get">
    {{ form.paginate_by.label }} {{ form.paginate_by }}
  </form>

  <table class="table twitter-user-list">
    <thead>
    <tr>
      <th scope="col">タイトル</th>
      <th scope="col">内容</th>
    </tr>
    </thead>
    <tbody>
    {% for article in contents %}
      <tr>
        <td class="align-middle">{{ article.title }}</td>
        <td class="align-middle">{{ article.content }}</td>
      </tr>
    {% endfor %}
    </tbody>
  </table>
  <nav aria-label="Page navigation">
    <ul class="pagination justify-content-center">
      {% if contents.has_previous %}
        <li class="page-item">
          <a href="{% url 'app:article_list' 1 %}" class="page-link">1</a>
        </li>
        <li class="page-item">
          <a href="{% url 'app:article_list' contents.previous_page_number %}" class="page-link"><</a>
        </li>
      {% else %}
        <li class="page-item disabled">
          <a href="#" class="page-link">1</a>
        </li>
        <li class="page-item disabled">
          <a href="#" class="page-link"><</a>
        </li>
      {% endif %}
      <li class="page-item active">
        <a class="page-link" href="#">{{ contents.number }}</a>
      </li>
      {% if contents.has_next %}
        <li class="page-item">
          <a href="{% url 'app:article_list' contents.next_page_number %}" class="page-link">></a>
        </li>
        <li class="page-item">
          <a href="{% url 'app:article_list' contents.paginator.num_pages %}" class="page-link">{{ contents.paginator.num_pages }}</a>
        </li>
      {% else %}
        <li class="page-item disabled">
          <a href="#" class="page-link">></a>
        </li>
        <li class="page-item disabled">
          <a href="#" class="page-link">{{ contents.paginator.num_pages }}</a>
        </li>
      {% endif %}
    </ul>
  </nav>

```