Published Date : 2019年10月5日7:42

Djangoで作ったブログのタグリストから関連記事を表示させる(2)
Show related articles from the tag list of Django blogs(2)


This blog has an English translation







前回からの続きです。

Continued from last time.

前回の記事の終わりにも書きましたが、今回はurls.pyの設定とtemplatesフォルダ内にtag.htmlを作り、タグ付けされた関連記事の表示をさせます。 それからindex.htmlとtag.htmlにタグリストを表示させて完了です。

As I wrote at the end of the last article, this time We'll settings urls.py and create a tag.html in the templates folder to display the tagged related articles. Then display the tag list on index.html and tag.html and it's done.



Responsive image




Responsive image



urls.pyの設定
Configuring urls.py

Demo Project
    demoblog
        __init__.py
        settings.py
        urls.py <- #今回、このurls.pyを書き換えます。
                   #This time, we will rewrite this urls.py.
        wsgi.py
    blogposts
        migrations
        static
        templates
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py <- #前回このviews.pyを書き換えました。
                    #We rewrote this views.py last time.
    db.sqlite3
    manage.py

urls.py
from django.conf.urls import include,url
from django.conf.urls.static import static

from posts import views

urlpatterns = [
    path('',views.index,name='index'),
    path('posts/',include('posts.urls')),
    path('posts/<post_id>/',views.post_detail,name="post_detail"),
    path('tag/<str:tagname>', views.related_posts, name="tagpost"), ###add
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

path('tag/<str:tagname>', views.related_posts, name="tagpost")

path(URLのパスとパラメーター、指定したURLにアクセスがあった場合に動かすviews.pyの関数、Djangoテンプレートで使う名前)

path (The path and parameters of the URL, the views.py functions to run when the specified URL is accessed, and the name to use in the Django template)


tag.htmlをテンプレートフォルダ内に作る。
Create templates/tag.html.

Demo Project
    demoblog
        __init__.py
        settings.py
        urls.py
        wsgi.py
    blogposts
        migrations
        static
        templates
            index.html
            tag.html <- #new create
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
    db.sqlite3
    manage.py

tag.html
<!DOCTYPE html>
<html lang="ja-jp">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Demo Blog</title>
    <!-- Bootstrap and Font Awesome css cdn -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.0/css/bootstrap.min.css" integrity="sha384-PDle/QlgIONtM1aqA2Qemk5gPOE7wFq8+Em+G/hmo5Iq0CCmYZLv3fVRDJ4MMwEA" crossorigin="anonymous">
    <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <style>
        .breadcrumb{
            background-color: white;
        }
        .breadcrumb-item + .breadcrumb-item::before {
            content: ">";
        }
    </style>
</head>
<body>
    <!-- tag list -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="#">demo blog</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
        {% load indices %}
        {% for tag in tags %}
        <li class="nav-item">
            <a class="nav-link" href="{% url 'tagpost' tag_names|indices:forloop.counter0 %}">{{ tag }}</a>
        </li>
        {% endfor %}
        </ul>
    </div>
    </nav>
    <!-- -->

    <!-- Breadcrumb -->
    <nav aria-label="breadcrumb">
    <ol class="breadcrumb">
        <li class="breadcrumb-item"><a title="Home" href="{% url 'index' %}"><i class="fa fa-home"> Home</i></a></li>
        <li class="breadcrumb-item active" aria-current="page"><a title="{{ tag_name }}" href="{% url 'tagpost' tag_name %}">tag > {{ tag_name }}</a></li>
    </ol>
    </nav>
    <!-- -->

    <!-- title list-->
    <div class="container text-center">
    <br><br>
    <h1>{{ tag_name }}</h1>
    <br>
    <hr>
    {% for article in article_list.all %}
        <a href="{% url 'post_detail' article_url_indices|indices:forloop.counter0 %}">{{ article.title }}</a>
        <hr>
    {% endfor %}
    </div>
    <!-- -->
    
    <!-- JQuery and Bootstrap cdn -->
    <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.0/js/bootstrap.min.js" integrity="sha384-7aThvCh9TypR7fIc2HV4O/nFMVCBwyIUKL8XCtKE+8xgCgl/PQGuFsvShjr74PBp" crossorigin="anonymous"></script>
</body>
</html> 

殆どの処理はviews.pyで行っているが、ポイントとなるのはこの部分の記述。

Most of the processing is done by views.py, but the point is to describe this part.

{% url 'tagpost' tag_names|indices:forloop.counter0 %}

この「indices」の部分はカスタムフィルタと言って、簡単にいうとDjangoに足らない部分を補うために、自分で独自の関数を作ることをいう。

This "indices" part is called a custom filter, which simply means creating your own functions to compensate for the lack of Django.


Djangoのテンプレートに、Pythonの配列参照(array[1]=”ホゲ”)のような機能が無いので、これを作ります。

Django templates don't have the functionality of Python array references (array [1] = "hoge"), so I'll create them.


まずカスタムフィルタを置く場所、tempatetagsフォルダーを作ります。

First, create a tempatetags folder for your custom filters.

blogposts/tempatetags
Demo Project
    demoblog
        __init__.py
        settings.py
        urls.py
        wsgi.py
    blogposts
        migrations
        static
        templates
            index.html
            tag.html
        templatetags <- #new create
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
    db.sqlite3
    manage.py

続いて、この「templatetags」ディレクトリの中のPythonファイルを、Djangoを動かしているPythonにパッケージとして認識させる為に、__init__.pyを作ります。

Next, create __init__.py to make Python on which Django is running recognize the Python files in this "templatetags" directory as packages.

この__init__.pyファイルの中には何も書かなくて大丈夫です。

You don't have to write anything in this __init__.py file.

blogposts/tempatetags/__init__.py
Demo Project
    demoblog
        __init__.py
        settings.py
        urls.py
        wsgi.py
    blogposts
        migrations
        static
        templates
            index.html
            tag.html
        templatetags
            __init__.py <- #new create
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
    db.sqlite3
    manage.py

続いてindices.pyを作ります。

Next, create indices.py.

blogposts/tempatetags/indices.py
Demo Project
    demoblog
        __init__.py
        settings.py
        urls.py
        wsgi.py
    blogposts
        migrations
        static
        templates
            index.html
            tag.html
        templatetags
            __init__.py
            indices.py <- #new create
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
    db.sqlite3
    manage.py
blogposts/tempatetags/indices.py
from django import template
register = template.Library()

@register.filter
def indices(py_list, idx):
    return py_list[int(idx)]

この関数の仕組みは単純。 Indices関数はPythonリストと別のリストのインデックス番号を引数として受け取ったら、 このリストの配列参照した値を返すだけです。

The mechanism of this function is simple. If the Indices function receives the index number of a Python list and another list as arguments, it simply returns the array referenced from that list.


このカスタムフィルタを使う際は先に

{% load indices %}
と書いてロードします。

To use this custom filter, first load it with

{% load indices %}
.

{% load indices %}
{% for tag in tags %}
<li class="nav-item">
    <a class="nav-link" href="{% url 'tagpost' tag_names|indices:forloop.counter0 %}">{{ tag }}</a>
</li>
{% endfor %}
{% url 'tagpost' tag_names|indices:forloop.counter0 %}

url 'tagpost'はurls.pyに設定したURLを呼び出します。

The url 'tagpost' calls the URL you set in urls.py.

このURLは<str型>のパラメータを受け取りますので、先程作ったカスタムフィルタを使って引数として渡します。

This URL takes a <str type> parameter, which you pass as an argument using the custom filter you just created.

tag_names|indices:forloop.counter0

この部分を馴染みのあるPython文法に直すと、こうなります。

If you translate this code into the familiar Python grammar, you get this.

for idx, tag in enumerate(tags):
    print(indices(tag_names, idx))

--> def indices(tag_names, idx):
        return tag_names[int(idx)]
--> """  >>> tag_names[1] --> 'Python' """

--> """ url 'tagpost' """
--> """ http://example.com/tag """
--> """ url 'tagpost' 'Python' """
--> """ http://example.com/tag/Python """

最後にindex.htmlにタグリスト表示機能を付け加えれば完成です。

Finally, add the tag list display function to index.html and you're done.

templates/index.html
Demo Project
    demoblog
        __init__.py
        settings.py
        urls.py
        wsgi.py
    blogposts
        migrations
        static
        templates
            index.html <- #add features to
            tag.html
        templatetags
            __init__.py
            indices.py
        __init__.py
        admin.py
        apps.py
        models.py
        tests.py
        urls.py
        views.py
    db.sqlite3
    manage.py
<!DOCTYPE html>
<html lang="ja-jp">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Demo Blog</title>
    <!-- Bootstrap and Font Awesome css cdn -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.0/css/bootstrap.min.css" integrity="sha384-PDle/QlgIONtM1aqA2Qemk5gPOE7wFq8+Em+G/hmo5Iq0CCmYZLv3fVRDJ4MMwEA" crossorigin="anonymous">
    <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <style>
        .breadcrumb{
            background-color: white;
        }
        .breadcrumb-item + .breadcrumb-item::before {
            content: ">";
        }
    </style>
</head>
<body>
    <!-- tag list -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="#">demo blog</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
        {% load indices %}
        {% for tag in tags %}
        <li class="nav-item">
            <a class="nav-link" href="{% url 'tagpost' tag_names|indices:forloop.counter0 %}">{{ tag }}</a>
        </li>
        {% endfor %}
        </ul>
    </div>
    </nav>
    <!-- -->

    <!-- Breadcrumb -->
    <nav aria-label="breadcrumb">
    <ol class="breadcrumb">
        <li class="breadcrumb-item active"><a title="Home" href="{% url 'index' %}"><i class="fa fa-home"> Home</i></a></li>
    </ol>
    </nav>
    <!-- -->

    <!-- title list-->
    <h1>demo blog title</h1>
    <br /><br />
    {% load static %}
    <img src="{% static "posts/001.jpg" %}" class="img-fluid rounded" alt="Responsive image"/>
    <hr>
    <br /><br />
    {% for post in posts.all %}
        <br /><br />
        <h4><a href="{% url 'post_detail' post.id %}">{{post.title}}</a></h4>
        {{post.published}}
        <br /><br />
        <hr>
    {% endfor %}
    
    <!-- JQuery and Bootstrap cdn -->
    <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.0/js/bootstrap.min.js" integrity="sha384-7aThvCh9TypR7fIc2HV4O/nFMVCBwyIUKL8XCtKE+8xgCgl/PQGuFsvShjr74PBp" crossorigin="anonymous"></script>
</body>
</html>

これでホームの記事一覧ページのナビバーに、タグリストが表示され、 タグをクリックすると、そのタグに関連付けされた記事の一覧のページに飛べるようになります。

Now, in the navigation bar of your home article list page, you'll see a tag list that you can click to go to the article list page associated with that tag.



Responsive image




Responsive image


あとは各々で色々試行錯誤して見やすいように、使いやすいようにカスタマイズしてくだちぃ。

Then, customize it so that it is easy to use and see through trial and error.




See You Next Page!