DjangoBrothers BLOG ✍️

2019/07/07

このエントリーをはてなブックマークに追加
Django フロントエンド クエリセット

【Django】django-taggitでタグ機能・関連記事機能を作る

ブログサイトでよく見るタグ機能の作り方です。

タグ機能は、自分でTagモデルを作って実装しても良いのですが、django-taggitという便利なパッケージがあるので、それを使って実装してみたいと思います。

django-taggitは、関連記事を取得できる機能もあるのでそれについても紹介します。

こんなBlogモデルがあったと仮定して、このBlogにタグをつけられるようにしていきます。

models.py

class Blog(models.Model):
    """ブログ記事"""
    title = models.CharField("タイトル", max_length=150)
    text= models.TextField("本文")

    def __str__(self):
        return self.title

django-taggitのインストール

まずは、パッケージをインストールします。

django-taggitをインストールする

$ pip install django-taggit==1.1.0

INSTALLED_APPSに追加します。

settings.py

INSTALLED_APPS = [
    ・・・
    'taggit',
    ・・・
]

この段階で一回マイグレートします。(あとからまとめてマイグレートすると、モデルの依存関係により不具合が出る可能性があります。)

マイグレート

$ python manage.py migrate

tagsフィールドを追加する

これで好きなモデルに、Tagを保存するフィールドを追加することができます。

models.py

from taggit.managers import TaggableManager  # 追加

class Blog(models.Model):
    """ブログ記事"""
    title = models.CharField("タイトル", max_length=150)
    text = models.TextField("本文")
    tags = TaggableManager(blank=True)  # 追加

    def __str__(self):
        return self.title

TaggableManagerはデフォルトでblank=Falseです。「ブログにタグを1つもつけない」ことを許容したい場合は、blank=Trueとします。

モデルを編集したら、マイグレーションファイルを作りマイグレートします。

Adminページで管理する

TaggableManager()フィールドは、他のフィールドと同様にAdmin画面で表示されます。 list_displayに表示させたい場合は、以下のように書きます。

admin.py

from django.contrib import admin
from .models import Blog

@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'tag_list')
    list_display_links = ('id', 'title')

    def get_queryset(self, request):
        return super().get_queryset(request).prefetch_related('tags')

    def tag_list(self, obj):
        return u", ".join(o.name for o in obj.tags.all())

このように、タグ名を入力すると、

自動で、タグオブジェクトを作ってくれます。画像のように、名称(name)フィールドとスラッグ(slug)フィールドに自動で文字列が入ります。

ただし、日本語でタグを入力した場合は、slugフィールドが空欄のタグオブジェクトができるので、slugは自分で入力してあげる必要があります。

オブジェクト操作

タグ情報へのアクセスの仕方です。

Blogに紐づいたタグ一覧を取得する

# タグ名称で取得
>>> blog_1.tags.names()
<QuerySet ['Python', 'Django']>

# slugで取得
>>> blog_1.tags.slugs()
<QuerySet ['python', 'django']>

「名称(str)」 or「 Tagオブジェクト」をadd()することで新規にタグを紐づけることができます。

タグを追加する

>>> blog_1.tags.add('programming')
>>> blog_1.tags.names()
<QuerySet ['Python', 'Django', 'programming']>

指定したタグとの紐付きを削除する

>>> blog_1.tags.remove('programming')
>>> blog_1.tags.names()
<QuerySet ['Python', 'Django']>

タグの紐付きを全削除する

>>> blog_1.tags.clear()
>>> blog_1.tags.names()
<QuerySet []>

タグで、フィルタリングもできます。

"Python", "Django" タグがついたBlog一覧を取得する

>>> Blog.objects.filter(tags__name__in=["Python", "Django"])
<QuerySet [<Blog: 1つ目の記事>, <Blog: 1つ目の記事>]>

# 記事を重複なく取得する
>>> Blog.objects.filter(tags__name__in=["Python", "Django"]).distinct()
<QuerySet [<Blog: 1つ目の記事>]>

関連記事を取得する

上記と同様に、関連記事を取得することもできます。

関連記事を取得する

>>> blog_1.tags.similar_objects()
[<Blog: 3つ目の記事>, <Blog: 2つ目の記事>]

similar_objects()は、関連度の強い順(共通のタグが多い順)でオブジェクトを取得できます。 取得されるのは、QuerySetではなく、リスト型なので注意が必要です。

Tagモデルを操作する

from taggit.models import Tagとすることで、Tagモデルの操作ができます。

タグを全て取得する

>>> from taggit.models import Tag
>>> Tag.objects.all()
<QuerySet [<Tag: Python>, <Tag: Django>, <Tag: programming>]>

特定のタグに紐づいたBlog一覧を取得する

# 「Pythonタグ」と紐づいたBlog一覧
>>> tag = Tag.objects.get(name="Python")
>>> Blog.objects.filter(tags=tag)
<QuerySet [<Blog: 1つ目の記事>, <Blog: 2つ目の記事>, <Blog: 3つ目の記事>]>

紐づいたBlogの多い順にタグを取得する

>>> from django.db.models import Count
>>> Tag.objects.all().annotate(blog_count=Count('taggit_taggeditem_items')).order_by('-blog_count')
<QuerySet [<Tag: programming>, <Tag: Python>, <Tag: Django>]>