Open2

Djangoで従テーブルのクエリーセットによるページネーションの実装方法

ponpon_maruponpon_maru

Djangoでページネーションを実装するにあたり、教本やWeb記事等では、ListViewのクラス変数であるpaginate_byをオーバーライドすることで、テンプレート側で{% if is_paginated%}でページネーションがある場合の制御を行ったり、page_obj等でページネーションの表示工夫をする事が出来ると目にします。

しかし従テーブルのクエリーセットによるページネーションを実装する方法について情報が無く、こちらで知見を得られたらなと思います。

ponpon_maruponpon_maru

具体的には、記事公開アプリケーション等でマイページ上に自分が投稿した記事をページネーション表示させるような場合を想定し、教本やWeb記事に記載されているような条件との相違点では、下記の条件を想定しています。

・主テーブルCustomUserに対して、従テーブルArticleが一対多の関係でリレーションシップを取る
・ビューではListViewを継承し、クラス変数modelにはCustomUserを指定し、クラス変数paginate_byの指定も行う
・get_queryset()のオーバーライドでは、マイページのユーザに紐づくArticleモデルのクエリーセットをテンプレートへ渡すようにする
・テンプレートでは、主テーブルCustomUserのクエリを{% for s in customuser_list %}で取り出し、従テーブルArticleのクエリを{% for article in s.name.all %}で取り出す。

#models.py

class CustomUser(AbstractUser):
    username = models.CharField(
        _("username"),
        max_length=30,
        help_text='Required 30 characters or fewer.',
        unique=True,
        error_messages={
            'unique': _("This Username already exists."),
        },)

    email = models.EmailField(
        _('email'),
        unique=True,
        error_messages={
            'unique': _("A user with that email address already exists."),
        },)

    class Meta:
        verbose_name_plural = 'CustomUser'


class Article(models.Model):
    post_user = models.ForeignKey(CustomUser, verbose_name='Post User', on_delete=models.CASCADE, related_name='name',)
    title = models.TextField(verbose_name='title', max_length=50,)
    content = MDTextField()
    thumbnail = models.ImageField(verbose_name='thumbnail', null=False, blank=False,)
#views.py

class MyPageView(LoginRequiredMixin, generic.ListView):
    template_name = 'mypage.html'
    model = CustomUser
    paginate_by = 6
    
    def get_queryset(self):
        articles = CustomUser.objects.filter(username=self.request.user).prefetch_related('name')
        return articles

<!--mypage.html-->
{% for s in customuser_list %}
<section class="u-content-space">
  <div class="container">
    <header class="text-center w-md-50 mx-auto mb-8">
      <h2 class="h1">Prototyping Works by {{ s.username }}</h2>
    </header>

    <div class="js-shuffle u-portfolio row no-gutters mb-6">
      {% for article in s.name.all %}
      <figure class="col-sm-6 col-md-4 u-portfolio__item" data-groups='["its-illustration"]'>
        <img class="u-portfolio__image_original" src="{{ article.thumbnail.url }}" alt="Image Description">
        <figcaption class="u-portfolio__info">
          <h6 class="mb-0">{{ article.title }}</h6>
        </figcaption>
      </figure>
      {% endfor %}
    
    <!--ページネーション処理-->
    {% if is_paginated %}
    <div class="row justify-content-between align-items-center mb-4">
      <div class="col-lg">
        <nav aria-label="Bootstrap Pagination Example">
          <ul class="pagination mb-0">
            <!--前ページへのリンク-->
            {% if page_obj.has_previous %}
            <li class="page-item">
              <a class="page-link" href="?page={{ page_obj.previous_page_number }}">
              <span class="mr-1 d-none d-sm-inline-block">&larr;</span> Previous
              </a>
            </li>
            {% endif %}

            <!--ページ数表示-->
            {% for page_num in page_obj.paginator.page_range %}
              {% if page_obj.number == page_num %}
                <li class="page-item active">
                  <a class="page-item" href="#">{{ page_num }}</a>
                </li>
              {% else %}
                <li class="page-item">
                  <a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a>
                </li>
              {% endif %}
            {% endfor %}

            <!--次ページへのリンク-->
            {% if page_obj.has_next %}
              <li class="page-item">
                <a class="page-link" href="?page={{ page_obj.next_page_number }}">
                  Next<span class="ml-1 d-none d-sm-inline-block">&rarr;</span>
                </a>
              </li>
            {% endif %}
          </ul>
        </nav>
      </div>
    </div>
    {% endif %}
    
  </div>
</section>
{% endfor %}