WagtailのSnippetsの基本的な使い方[Django]
Wagtailのサイト上で使い回すコンテンツを設定する『Snippets』の基本的な使い方について解説します。
Snippetとは、『断片』を意味する英単語ですが、プログラミングにおいては、再利用可能な小さなコードのまとまりを意味することがあります。
WagtailにおけるSnippetは、Pageの中で利用されるパーツを構成するための仕組みです。例えば、記事の中で利用する広告を登録したり、目を引くような特殊な記事内のバナーを設定したりすることができます。それらのSnippetは、それ単体ではPageを構成しませんが、Pageの中で何度でも再利用できるパーツとしてAdminから登録できるようになっています。
この記事では、WagtailにおけるSnippetの基本的な使い方を紹介します。
Template TagとしてSnippetを使う方法
まずは簡単なSnippetの例を見て、Snippetの基本を理解しましょう。
一般的なブログサイトで、サイドバーに画像形式のバナーを差し込む場合を想定します。この場合、バナーの画像の登録と、そのリンク先のURLを登録する必要があります。今回は簡便に、登録したすべてのバナーをサイドバーに表示させるような例を示します。
このSnippetは以下のように実装することができます。ads
というアプリフォルダの中で作業を進めていることを想定しています。
# ads/models.py
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.snippets.models import register_snippet
@register_snippet
class Ad(models.Model):
name = models.CharField(max_length=255)
url = models.URLField(null=True, blank=True)
image = models.ImageField(upload_to='image/ads')
panels = [
FieldPanel('name'),
FieldPanel('url'),
FieldPanel('image'),
]
def __str__(self):
return self.name
モデル自体は通常のDjangoのモデルと同じです。WagtailのAdminからそれぞれ入力できるようにFieldPanel
を設定しています。@register_snippet
を利用することで、このモデルをSnippetとして登録することができます。
今度はこのSnippetをTemplate Tagとして利用できるようにしていきます。ads
アプリ内にtemplatetags
というフォルダを作成し、その中でTemplate Tagを追加していきます。
# ads/templatetags/ad_tags.py
from django import template
from ads.models import Ad
register = template.Library()
@register.inclusion_tag('ads/tags/ads.html', takes_context=True)
def ads(context):
return {
'ads': Ad.objects.all(),
'request': context['request'],
}
@register.inclusion_tag()
は二つの引数を取ります。一つ目がTemplate Tagで利用するテンプレート名で、二つ目はrequest contextを利用するかどうかのBooleanです。ここでtakes_context=True
を指定しないと、Wagtailの他のTemplate Tagが利用できない場合があるため、基本的にはこの設定が良いでしょう。
今回は簡便に、Ad.objects.all()
ですべての登録したAdインスタンスを取得していますが、例えば表示のオンオフなども登録できるようにしてフィルタリングすることも可能です。
続いて、テンプレート側を作成しましょう。ads
アプリ内のtemplates/ads
フォルダの中に、tags/ads.html
を作成します。
<!-- ads/templates/ads/tags/ads.html -->
{% for ad in ads %}
<section>
<a href="{{ ad.url }}">
<img src="{{ ad.image.url }}">
</a>
</section>
{% endfor %}
このテンプレートファイルには、ad_tags.py
で指定したパラメータが渡されますので、その情報を利用して表示方法を指定します。今回は登録したAdのリンク付きの画像を並べるだけにしてあります。
最後に、このTemplate Tagを指定の箇所から呼び出してみます。以下の例では、home
アプリのトップページで使用する例です。
<!-- home/templates/home.html -->
{% load wagtailcore_tags ad_tags %} <!-- 登録したad_tagsをロードする -->
{% block content %}
{% ads %} <!-- ここにads.htmlが表示される -->
{% endblock %}
以上が、SnippetをTemplate Tagとして呼び出す方法です。
Adminからは以下のように、SnippetsメニューからAds snippetを登録できるようになっており、Adモデルで作成したフィールドに情報を入力できるようになっています。
Pageに紐づくSnippetを使う方法
Template TagとしてSnippetを使う方法では、Snippetを登録したら固定のテンプレートで表示されます。そのため、使い所としてはヘッダーやサイドバーなど、共通で利用するテンプレート内で利用するか、トップページの一部分だけで利用する場合などが考えられます。
一方で、それぞれの記事などでSnippetを個別に指定できるようにしたい場合もあります。その場合は、Pageモデルに紐づくようにSnippetを登録し、Adminから表示するSnippetを選択できるようにします。
以下の例では、一般的によく利用されるカテゴリを実装してみます。ブログサイトなどで個別の記事にカテゴリを割り当てる場合を想定しています。この場合、BlogCategory
モデルを作成してSnippetとして登録し、BlogArticle
モデルに紐付けします。
# blogs/models.py
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.models import Page
from wagtail.snippets.models import register_snippet
from wagtail.snippets.edit_handlers import SnippetChooserPanel
# カテゴリを表現するモデル、Snippetとして登録
@register_snippet
class BlogCategory(models.Model):
name = models.CharField("Category name", max_length=50)
panels = [
FieldPanel("name"),
]
def __str__(self):
return self.name
class BlogArticle(Page):
...
# カテゴリを紐づける、blogアプリのBlogCategoryモデルを指定
category = models.ForeignKey(
'blog.BlogCategory',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
content_panels = Page.content_panels + [
...
SnippetChooserPanel('category'), # AdminでSnippetを選択するパネルを追加
]
カテゴリの名前を登録するモデルを定義し、models.ForeignKey
でPage
モデルと紐付けをします。SnippetChooserPanel
は、Admin上で以下の画像のように登録済みのSnippetから選択できるようにする設定です。
テンプレート上からは通常通り、{{ self.category.name }}
の形で情報を呼び出すことができます。
Snippetを検索できるようにする
Snippetが増えてくると、目的のSnippetを探すのに時間がかかるようになります。そこで、特定の情報でSnippetを検索できるようにしましょう。
やることは大きく二つです。検索可能にしたいモデルでwagtail.search.index.Indexed
を継承し、search_fields
で検索対象のフィールドを指定するだけです。
試しに、Template Tagを利用した方法のときに作成したAdモデルを検索可能にしてみます。
# ads/models.py
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.snippets.models import register_snippet
from wagtail.search import index # 追加
@register_snippet
class Ad(index.Indexed, models.Model): # index.Indexedを追加
name = models.CharField(max_length=255)
url = models.URLField(null=True, blank=True)
image = models.ImageField(upload_to='image/ads')
panels = [
FieldPanel('name'),
FieldPanel('url'),
FieldPanel('image'),
]
# search_fieldsを追加、partial_match=Trueは部分一致検索の設定
search_fields = [
index.SearchField('name', partial_match=True),
]
def __str__(self):
return self.name
これで、Admin上でSnippetが検索可能になります。この実装で、Adminに検索ボックスが表示されます。
検索ボックスにテキストを入力することで、条件にマッチしたSnippetが表示されます。
Snippetにタグをつける
Snippetにも、Pageモデル同様にタグをつけることができます。 Snippetに登録した広告の管理などでタグをつけて整理するなどの便利な活用方法があります。
上記のAdモデルを修正してみます。タグ自体のモデルであるAdTag
を作成し、Ad
モデルのtags
フィールドに紐づけます。
# ads/models.py
from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.search import index
from wagtail.snippets.models import register_snippet
# Tagに必要なモジュールを追加
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from taggit.models import TaggedItemBase
from taggit.managers import TaggableManager
# タグを表現するモデル、TaggedItemBaseを継承する
class AdTag(TaggedItemBase):
# タグを紐づけたいモデルをParentalKeyで指定する
# 今回はadsアプリの中のAdモデルを指定
content_object = ParentalKey('ads.Ad', on_delete=models.CASCADE, related_name='tagged_items')
@register_snippet
class Ad(index.Indexed, ClusterableModel): # models.ModelからClusterableModelに変更
name = models.CharField(max_length=255)
url = models.URLField(null=True, blank=True)
image = models.ImageField(upload_to='image/ad')
tags = TaggableManager(through=AdTag, blank=True) # AdTagと紐付けしたフィールドを追加
panels = [
FieldPanel('name'),
FieldPanel('url'),
FieldPanel('image'),
FieldPanel('tags'), # Adminの入力フィールドを追加
]
search_fields = [
index.SearchField('name', partial_match=True),
]
def __str__(self):
return self.name
通常のPageモデルにタグをつけるパターンとほぼ同じですが、tags
フィールドにtaggit.manager.TaggableManager
を利用する点に注意です。
Adminからは、TAGSのフィールドで、,
で区切りを入れてテキストを入力すると、このSnippetに複数のタグをつけることができるようになります。