【Python】Djangoで無限スクロールを実装する|コードの紹介と解説

【Python】Djangoで無限スクロールを実装する|コードの紹介と解説 プログラミング
【Python】Djangoで無限スクロールを実装する|コードの紹介と解説
スポンサーリンク

こんにちは、こがたです。

PythonのWeb開発フレームワーク「Django」で無限スクロールを実装する方法を紹介します。

対象読者
  • Djangoで無限スクロールを実装したい
  • Pagenationを利用したデータの流れを理解したい
  • 大量のデータでPagenationを利用したらどうなるのか知りたい

最初に無限スクロールとページネーション、それぞれのメリットデメリットを説明します。
開発にあたりどちらが向いているのか検討材料にしてください。
実装コードだけ知りたい方は読み飛ばしてください。

次にDjangoの必要部分のコードを紹介しながら解説していきます。
Djangoの基礎知識がある上での解説になります。

他言語でも同じようにデータを利用すれば実装できるので参考になるでしょう。

最後に紹介するコードの懸念点を説明します。

無限スクロールの用途

一覧ページを表示するのは「無限スクロール」と「ページネーション」があります。

それぞれのメリット、デメリットを説明するので、どちらがよいのか検討してみてください。

無限スクロール

TwitterやFacebookなどのように下にスクロールしていけばデータが読み込まれて表示される方法です。

メリット
  • 流してみることができる
  • 利用者の手間が少ない

なにかをクリックしたりすくことなく、次のデータを見ることができるので、ユーザビリティは高いです。

デメリット
  • フッターが見えない
  • 一気に飛ばしてみることができない

データがある限りスクロールすることができるので、フッターを表示することは基本的にできません。
※フロントをカスタマイズすれば表示可能

スクロールすればみることができますが、逆にいうとスクロールをしなければいけません。
先の情報をみたい場合、その分スクロールしなければいけません。

こがた
こがた

Twitterで一年前の情報を探そうとしてもスクロールだと大変ですよね、、、

そのため、欲しい情報を見つけるための検索フォームがあるとよいでしょう

ページネーション

Google検索などよく利用されている方法です。

ページネーション
メリット
  • 情報がどこにあるのか明確になる
  • 先のデータに飛びやすい

一般的にページネーションで次のページに移動するとURLが変わります。
つまり、特定の一覧ページをブックマークすることができます

また、10ページ先などに簡単に遷移することができます。

デメリット
  • 次にページにいくのにクリックという手間がかかる
  • 先のページが読まれづらくなる

次のページに移動するためにクリックやURLの変更が必要になります。

そのため、無限スクロールと比べて手間となり、ページビューが落ちやすいです。

こがた
こがた

Google検索でも、ほとんど最初の10件しか見られません。

Djangoで無限スクロールを実装

これから実際に無限スクロールを実装していきます。

この記事ではDjango3.1を使用しています。

必要な外部モジュール

どれもフロントサイドものになります。

jQueryはこちらからダウンロードしてください。
この記事では「jquery-3.5.1.min.js」を使用します。

Waypoints、infinite-scrollはこちらからダウンロードできます。
(Git,npmなど)

使用するファイル
  • lib/jquery.waypoints.min.js
  • lib/shortcuts/infinite.min.js

それではDjangoのコードにはいっていきます。
ここでは商品一覧ページでを無限スクロールを実装します。

items」というアプリ名にしています。

※Djangoの基本はおさえている前提になります

models

シンプルな商品モデルを利用します。

# items/models.py

from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=100)
    description  = models.CharField(max_length=6000)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

商品名、説明分、作成日をもったシンプルなモデルです。

views

次にViewを実装します。

# items/views.py

from django.views import generic
from .models import Item

class ItemListScroll(generic.ListView):
    model = Item
    template_name = 'items/item_list_scroll.html'
    paginate_by = 10

    def get_queryset(self):
        return self.model.objects.all().order_by('created_at')

ここではListView(汎用ビュー)を使用しています。

paginate_by」で一回で表示するデータ数を設定しています。
これはページネーションで表示する方法と同様です。

公式ドキュメントはこちら

get_queryset」は親クラスのメソッドでオーバライドしています。

オーバーライドしなければ↓↓↓のように動きます。

 def get_queryset(self):
        return self.model.objects.all()

つまり、Itemオブジェクトを全件取得します。

オーバーライドしたのは、作成日順に表示するためです。
(必要ない場合は削除してください)

html

表示画面を作成していきます。

ここでは共通パーツ(base.html)を作成して読み込んでいます。
また、BootStrapも読み込んでいます。

<!doctype html>
<html lang="ja">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
          integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
    
    {% load static %}
    <script src="{% static 'items/js/jquery-3.5.1.min.js' %}"></script>
    <script src="{% static 'items/js/jquery.waypoints.min.js' %}"></script>
    <script src="{% static 'items/js/infinite.min.js' %}"></script>
    {% block extrajs %}{% endblock %}

    <title>商品サンプル</title>
</head>
<body>
    <!-- メインコンテント -->
    <div class="container mt-3">
        {% block content %}{% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"
            integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"
            integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"
            crossorigin="anonymous"></script>
</body>
</html>

重要なのは12行目〜16行目です。

12行目〜15行目で準備した「jQuery」「Waypoints」「infinate-scroll」を読み込んでいます。

16行目の「{% block extrajs %}{% endblock %}」で各ページごとのJSコードを記載することができるようになります。


商品一覧ページ(items/templates/items/item_list_scroll.html)はこのようになります。

{% extends "items/base.html" %}
{% block content %}
<div class="infinite-container">
    <div>
        <p>タイトル</p>
    </div>

    {% for item in object_list %}
    <div class="infinite-item">
        <p>**************</p>
        <p>{{ item.name }}</p>
        <p style="width:70%; word-wrap:break-word;">{{ item.description }}</p>
        <br>
        <br>
    </div>
    {% endfor %}
</div>

<div class="loading" style="display: none;">
    読み込み中...
</div>

{% if page_obj.has_next %}
<a class="infinite-more-link" href="?page={{ page_obj.next_page_number }}">さらに読み込む</a>
{% endif %}
{% endblock %}


{% block extrajs %}
<script>
var infinite = new Waypoint.Infinite({
    element: $('.infinite-container')[0],
    onBeforePageLoad: function () {
    $('.loading').show();
    },
    onAfterPageLoad: function ($items) {
    $('.loading').hide();
    }
});
</script>
{% endblock %} 

次のデータを読み込んでいる最中に「読み込み中…」が表示されます。

一覧のコンテンツに「infinite-container」クラスを指定してください。
読み込まれる各データコンテンツのクラスは「infinite-item」を指定します。

ここで注意するのは「infinite-item」に内包的なDOMは利用できないということです。

「li」に「infinite-item」をすると新しいデータを読み込むとこのようになります。

<div class="infinite-container">
<ul>
    <li class="infinite-item">初期表示データ</li>
    <li class="infinite-item">初期表示データ</li>
    <li class="infinite-item">初期表示データ</li>
    <li class="infinite-item">初期表示データ</li>
</ul>
    <li class="infinite-item">新しく読み込んだデータ</li>
    <li class="infinite-item">新しく読み込んだデータ</li>
    <li class="infinite-item">新しく読み込んだデータ</li>
    <li class="infinite-item">新しく読み込んだデータ</li>
</div>

このように「ul」の外で追加されていきます。

page_obj」はページの情報を保持しています。

page_objの情報例
  • has_next :次のページの有無
  • has_previous :前のページの有無
  • next_page_number :次のページ番号
  • previous_page_number :前のページ番号

「next_page_number」「previous_page_number」は次、前のページの有無を確認した上で利用してください。
エラーが発生します。

実際に読み込んだらこのようになります。

scroll
こがた
こがた

デザインはあててないので、シンプルな表示になっています

無限スクロールコードの懸念点

勘の鋭い方は気づいたかもしれませんが、views.pyの「get_queryset」で全てのデータを取得しているとう点です。

def get_queryset(self):
        return self.model.objects.all().order_by('created_at')
こがた
こがた

読み込む度に全データを読み込んでいたらレスポンス遅くならない?

そこで以下のviewsに変更して100万データで試してみました。

from django.shortcuts import render
from django.views import generic

import random, string

from .models import Item


class ItemListScroll(generic.ListView):
    model = Item
    template_name = 'items/item_list_scroll.html'
    paginate_by = 10

    def get_queryset(self):
        return self.model.objects.all().order_by('created_at')


class ItemListAll(generic.ListView):
    model = Item
    template_name = 'items/item_list_all.html'

    def get_queryset(self):
        return self.model.objects.all().order_by('created_at')


class MakeData(generic.TemplateView):
    """ユーザーの詳細ページ"""
    template_name = 'items/make_data.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs) 

        for i in range(1000000):
            item = Item()
            item.name = 'test_' + str(i+1)
            item.description = ''.join(random.choices(string.ascii_letters + string.digits, k=2000))
            item.save()

        return context

「ItemListAll」「MakeData」のhtmlはDjangoの基本的な書き方になるので省略します。

「MakeData」関数でランダムな2000文字の説明文を持った商品を100万件作成します。

この状態で全件を1ページで表示する「ItemListAll」を実行すると表示されるまで1分以上かかりました、、、

これまで紹介してきた無限スクロールのページを表示してみると、読み込む度に1〜2秒ほどかかるようになりました。

こがた
こがた

100万件で1,2秒ならギリギリ許容範囲内かと思います!

html作成時間が短縮できるので実行時間が短く済むと予想されます。

効率的にWEBシステムを開発できるようになる方法

WEBフレームワークを理解しただけでは、WEBシステムを公開することはできません。。。

理解しておく必要があること
  • ネットワーク(セキュリティ等)について
  • ハードウェア(サーバ等)について
  • ミドルウェアについて:Linux,Windows
  • ソフトウェアについて:Webサーバ,プログラミング言語,フレームワーク
  • データベース

Python初心者の方はPyQで学習することをオススメします!
Pythonができることを広く浅く学習することができます!

ただし、プログラミング中級者にとっては多少物足りない内容になっています。

プログラミング中級者の方は自分で調べながら開発していくことができる状態の場合です。
中級者の方が学習する場合は『Udemy』で学習するのがオススメです!

10万以上の講座があり、自分のレベルに合わせてピンポイントに学習することができます。
セール中であれば、1,000円代まで値下げされるので安く、しっかり学ぶことが可能です!

最後に

無限スクロールはユーザビリティの高い表示方法です。

実装方法がムズカしくみえたかもしれませんが、やっていることは単純です。

仕組みを理解することができたらどの言語でも実装できるようになります。

開発の参考になれば幸いです。

最後まで読んでくださり、ありがとうございました!!!

コメント

タイトルとURLをコピーしました