Django開発~ブログ構築編5~カスタムユーザーの続きです。
django-tables2を使って、ブログにページネーション機能、テーブルソート機能を実装したいと思います。
django-tables2とは
django-tables2は、テーブル一覧を自動で生成してくれるサードパーティ製のモジュールです。
主に以下の機能があります。
・ページネーション機能の搭載
・テーブルソート機能の搭載
モジュールの追加
requirements.txtにモジュールを追加してみましょう。
1 2 3 4 5 6 7 8 9 10 11 |
vi requirements.txt Django>=2.1.3,<2.2.0 djangorestframework>=3.9.0,<3.10.0 flake8>=3.6.0,<3.7.0 psycopg2>=2.7.5,<2.8.0 Pillow>=5.3.0,<5.4.0 pytest-django==3.5.1 django-cors-headers==2.4.0 django-tables2==2.1.0 |
モジュールを適用するには、dockerコンテナをビルドする必要があります。
1 2 |
docker-compose stop docker-compose up -d --build |
ビルド成功後、settings.pyにdjango-tablesを追加してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
vi app/app/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # myapp 'blog', 'accounts', # sitemap 'django.contrib.sites', 'django.contrib.sitemaps', # django-tables2 'django_tables2', ] |
tables.pyを作成
blogアプリケーション配下にtables.pyファイルを作成しましょう。
1 |
touch app/blog/tables.py |
このファイルに、画面に出力するテーブルの設定を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
vi app/blog/tables.py import django_tables2 as tables from .models import Post class PostTable(tables.Table): id = tables.Column(verbose_name='ID') title = tables.Column(verbose_name='Title') body = tables.Column(verbose_name='Description') author = tables.Column(verbose_name='Poster') class Meta: template_name = 'blog/post_table.html' row_attrs = { 'id': lambda record: record.id, 'class':'oneHeaderTable__tr', 'url': lambda record: record.get_absolute_url, } |
このPostTableクラスにPostデータを渡すと画面に表示するテーブル一覧の元になります。
views.pyの変更
続いて、Viewのpost_listに変更を加えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
vi app/blog/views.py from django.contrib.auth import get_user_model from django.views.generic import CreateView, UpdateView, DeleteView from django.shortcuts import render, get_object_or_404, redirect from django_tables2 import RequestConfig # new from .models import Post from .forms import PostForm from .tables import PostTable # new # change def post_list(request): posts = Post.published.all().filter(author__id=request.user.id) table = PostTable(posts) // ページネーション数3 RequestConfig(request, paginate={'per_page': 3}).configure(table) return render(request, 'blog/post/list.html', {'posts': posts, 'table': table}) |
filterメソッドを使うとデータ取得の条件を指定できます。ここでは、ログインユーザーのデータのみ取得し、postsに格納しました。ログインユーザーの情報は、requestプロパティの「user」に入っています。
postsをPostTableに渡してtable(テーブルオブジェクト)を作成し、tableデータを画面に返します。
base.htmlの変更
font awesome を導入しましょう。
font awesomeは、Webアイコンを提供してくれます。この機能を使うためには、CDNで読み込む必要があるので、base.htmlに変更を加えましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
vi app/blog/templates/blog/base.html {% load static %} <!DOCTYPE html> <html> <head> <title>{% block title%}{% endblock %}</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link href="{% static 'css/blog.css' %}" rel="stylesheet"> <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet"> </head> <body> <div class="container"> <div class="row"> <div id="content" class="col-8"> {% block content %} {% endblock %} </div> <div id="sidebar" class="col-4"> <h2>My blog</h2> <p>This is my blog.</p> </div> </div> </div> <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.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </body> </html> |
post_table.htmlの作成
tables.pyを作成した時、template_nameに「post_table.html」を指定したので、このファイルを作成しましょう。このテンプレートにtable(テーブルオブジェクト)を渡します。
1 |
touch app/blog/templates/blog/post_table.html |
django-tables2のテンプレートの一つである「bootstrap4.html」を元にテンプレートを作成します。更に、「updateボタン」、「deleteボタン」、「詳細画面へのリンク」を追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
vi app/blog/templates/blog/post_table.html {% load django_tables2 %} {% load i18n %} {% block table-wrapper %} <div class="table-container"> {% block table %} <table {% render_attrs table.attrs class="table" %}> {% block table.thead %} {% if table.show_header %} <thead class="thead-default" {{ table.attrs.thead.as_html }}> <tr> {% for column in table.columns %} <th {{ column.attrs.th.as_html }}> {% if column.orderable %} <a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a> {% else %} {{ column.header }} {% endif %} </th> {% endfor %} <th><i class="fas fa-edit text-primary col-6"></i></th> <th><i class="fas fa-trash-alt text-primary col-6"</i></th> </tr> </thead> {% endif %} {% endblock table.thead %} {% block table.tbody %} <tbody {{ table.attrs.tbody.as_html }}> {% for row in table.paginated_rows %} {% block table.tbody.row %} <tr scope="row" {{ row.attrs.as_html }}> {% for column, cell in row.items %} <td {{ column.attrs.td.as_html }}><a href="{{row.attrs.url}}">{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</a></td> {% endfor %} <td><a href="{% url 'blog:update' row.attrs.id %}" class="btn btn-success">Update</a></td> <td><a href="{% url 'blog:delete' row.attrs.id %}" class="btn btn-danger">Delete</a></td> </tr> {% endblock table.tbody.row %} {% empty %} {% if table.empty_text %} {% block table.tbody.empty_text %} <tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr> {% endblock table.tbody.empty_text %} {% endif %} {% endfor %} </tbody> {% endblock table.tbody %} {% block table.tfoot %} {% if table.has_footer %} <tfoot {{ table.attrs.tfoot.as_html }}> <tr> {% for column in table.columns %} <td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td> {% endfor %} </tr> </tfoot> {% endif %} {% endblock table.tfoot %} </table> {% endblock table %} {% block pagination %} {% if table.page and table.paginator.num_pages > 1 %} <nav aria-label="Table navigation"> <ul class="pagination justify-content-center"> {% if table.page.has_previous %} {% block pagination.previous %} <li class="previous page-item"> <a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}" class="page-link"> <span aria-hidden="true">«</span> {% trans 'previous' %} </a> </li> {% endblock pagination.previous %} {% endif %} {% if table.page.has_previous or table.page.has_next %} {% block pagination.range %} {% for p in table.page|table_page_range:table.paginator %} <li class="page-item{% if table.page.number == p %} active{% endif %}"> <a class="page-link" {% if p != '...' %}href="{% querystring table.prefixed_page_field=p %}"{% endif %}> {{ p }} </a> </li> {% endfor %} {% endblock pagination.range %} {% endif %} {% if table.page.has_next %} {% block pagination.next %} <li class="next page-item"> <a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}" class="page-link"> {% trans 'next' %} <span aria-hidden="true">»</span> </a> </li> {% endblock pagination.next %} {% endif %} </ul> </nav> {% endif %} {% endblock pagination %} </div> {% endblock table-wrapper %} |
list.htmlの変更
最後にlist.htmlを変更しましょう。django_tables2をインクルードします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
vi app/blog/templates/blog/post/list.html {% extends "blog/base.html" %} {% load django_tables2 %} {% block title %}My Blog{% endblock %} {% block content %} {% if user.is_authenticated %} <div class="row"> <h1 class="col-6">My Blog</h1> <a href="{% url 'logout' %}" class="btn btn-danger float-right col-2 mr-2 mb-2">Log Out</a> <a href="{% url 'blog:create' %}" class="btn btn-primary float-right col-2 mb-2">POST</a> </div> <div> <!-- post_table.htmlにtableを渡す --> {% render_table table %} </div> {% else %} <p>You are not logged in</p> <div class="row"> <a href="{% url 'login' %}" class="btn btn-primary col-2 mr-2">Log In</a> <a href="{% url 'signup' %}" class="btn btn-success col-2">SignUp</a> </div> {% endif %} {% endblock %} |
動作確認
ここまでお疲れ様でした。動作確認して見ましょう。
「localhost:8000」にアクセスしてください。

綺麗なテーブルが出力されていますね。Titleの「about」をクリックしてみます。

肝心なページネーションが確認できませんね。投稿数を増やしてみましょう。投稿数が4つ以上になるとページネーション状態になります。

ページの下の方に「next」が表示されましたね。押してみましょう。

2ページ目に画面遷移できましたね。今度は、1ページ目に戻り、「ID」リンクを押下してください。

「昇順」でソートできましたね。もう一度押下すると昇順になります。
動作は問題なさそうですね。今回は以上になります。
次回
次回は、検索フォームをブログに実装します。お楽しみに^^
コメントを残す
コメントを投稿するにはログインしてください。