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>]>