【Django】TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()
【Django】TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()
こんなエラーが出てきたので翻訳機に掛けてみました。
TemplateResponseMixin は 'template_name' の定義か 'get_template_names() の実装を必要とします。
(意訳)TemplateResponseMixinと言う所にデータが渡ってきたけど必要な情報揃ってないよ。
今回解決に至った方法
まぁね、まだまだな訳です。
Django初心者、参考図書のルート上にある事はやりたい様に動かせても、テキストのルートを少し外れると不明な事ばかりです。今回はそんな問題の一端でした。
#views.py
import logging
from django.urls import reverse_lazy
from django.contrib import messages
from django.views import generic
from django.shortcuts import render
from .models import Company
from .forms import InquiryForm,
logger = logging.getLogger(__name__)
class IndexView(generic.TemplateView):
template_name = "websites/index.html"
class InquiryView(generic.FormView):
template_name = "websites/inquiry.html"
form_class = InquiryForm
success_url = reverse_lazy('company:inquiry')
def form_valid(self, form):
form.send_email()
messages.success(self.request, 'メッセージを送信しました。')
logger.info('Inquiry sent by {}'.format(form.cleaned_data['name']))
return super().form_valid(form)
class ServiceView(generic.FormView):
template_name = "websites/service.html"
この中の28行目【class ServiceView(generic.FormView):】を【class ServiceView(generic.FormView):】と修正するとエラーの発生はなくなります。
class ServiceView(generic.TemplateView):
template_name = "websites/service.html"
この方法だと website/service.htmlに記載した値をurl.pyで関連付けたアドレスで表記してくれます。
#urls.py
from django.urls import path
from . import views
app_name = 'web'
urlpatterns = [
path('', views.IndexView.as_view(), name="index"),
path('inquiry', views.InquiryView.as_view(), name="inquiry"),
path('service', views.ServiceView.as_view(), name="service"),
こんな感じでurls.pyを書いていれば、https://ドメイン/service にアクセスすれば websites/service.html を表示します。でもやりたのはそこじゃないんです。
FormView と書いてみたのはDBに格納した値を表示したかったからなんですよね。
WordPressのようにSQLにHTMLを格納しページに表示するための構成
今回はSQLに【 contents 】というテーブルを用意し次のようなフィールドを用意しました。
- types:記載ページを指示(informationかserviceかといったフラグ)
- name:記載内容(contents)のタイトル。WEBに表現する時はhタグをつけて飾る予定
- contents:記載するHTMLを格納する場所。bootstrapのルールに従っておけばテンプレート変更で色々変更出来るはず。
- sort_number:typesの中の並び替え。コンテンツを小分けにした方が融通を利かせられるので用意。
- switch:WEBへの表示対象とするかを表すマーカー。Falseにしておけば表示しないのでWEBサイトの変更に活用できる。
これをmodels.pyに表現するとこんな感じ。
#models.py
from django.db import models
from django.core.validators import RegexValidator
class Contents(models.Model):
"""CMS_WEB記載内容/要素"""
CATEGORY_CHOICES = (
(1, 'インフォ'),
(2, 'サービス'),
(3, 'プロジェクト'),
(4, 'プロフィール'),
)
types = models.IntegerField(verbose_name='記載箇所', choices=CATEGORY_CHOICES)
name = models.CharField(verbose_name="コンテンツ名", max_length=100, blank=False, null=True)
contents = models.TextField(verbose_name="HTMLコード", blank=False, null=True)
sort_number = models.IntegerField(verbose_name='並び順', blank=False, default=1)
switch = models.BooleanField(verbose_name='ON/OFF', blank=False, default=True)
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
class Meta:
verbose_name_plural = 'Contents'
def __str__(self):
return self.name
この状態でマイグレーションを実行すれば設定したSQLサーバーにテーブルを作成してくれます。
Djangoではこうしてmodels.Model に紐づけたテーブル構成はQuerySetとして扱う事が出来る様です。
で、Views.pyではこのQuerySetを呼出して欲しい値の抽出を行います。
#views.py
(前略)
def ServiceView(request):
data = Contents.objects.filter(switch=1).filter(types=2).order_by('sort_number')
params = {
'title': '事業内容',
'data': data
}
return render(request, 'websites/service.html', params)
この時のurls.pyは下のようにします。
#urls.py
(前略)
urlpatterns = [
(中略)
path('service', views.ServiceView, name="service"),
]
これで、https://ドメイン/service にアクセスしてきた場合 contentsテーブルの値にフィルターかけて抽出したものをservice.htmlに渡して表示する事が出来ます。
CMSの原型がこれで完成です。
まとめ
ここまででなかなか手こずったのは【class】と【def】の違いと記載方法でした。
Laravelを道具にしてた私からすると【def】の書き出しになじみがなく、全てClassで書いていたのですが[ class ServiceView(request): ] とするとエラーなんですよね。classではrequestを呼び込めないのか、動線が異なるのか、ここら辺はまだ「理解してる」と言い難いです。
まぁ作りながら理解してくしかないですね。
今日も頑張りましょう!

