本記事では「初めてのDjango(発展編)」として、初めてDjangoというフレームワークを使用してWebアプリケーションを作ってみたい!という方向けに、簡単なアプリケーションを作成しながら基本的な知識をご紹介していきます。
本シリーズの全容はこのようになっております。
- (入門編 #01)単一ページのアプリケーション作成まで
- (入門編 #02)CSSの適用からベーステンプレートの作成まで
- (入門編 #03)コンテキスト情報とテンプレートタグの活用
- (入門編 #04)お問い合わせフォームの設置、メール送信処理まで
- (入門編 #05)お問い合わせ送信後にメッセージを表示させる
- (入門編 #06)アプリケーションをGAEにデプロイ
- (発展編 #01)データベースの設定を行う
- (発展編 #02)カスタムユーザーモデルの定義、マイグレーション
- (発展編 #03)django-allauthで認証機能を実装する
- (発展編 #04)ListViewを用いてモデル情報を描写する
- (発展編 #05)CreateViewを使用してデータベース更新
- (発展編 #06)DetailViewを用いて詳細表示 ←イマココ
- (発展編 #07)UpdateViewを用いてデータベース編集
- (発展編 #08)DeleteViewを用いてデータベースのレコード削除
・DetailViewを使用して、個別詳細ページの作成を行う
本記事単体でご覧になる方へ
本記事は連作となっており、こちら(GitHub)のコードの続きとしてご紹介させていただきます。本記事単体でもご覧いただけますが、もし上記のコードを引用して学びたい方は下記の流れに沿って準備を行なってください。
先ほどダウンロードしたファイル群を任意のディレクトリに配置します。
その後、プロジェクトのルートディレクトリ(manage.pyがあるとこ)に settings.json を新規作成し、下記の内容を記述してください。
{
"SECRET_KEY":"<djangoのシークレットキー>",
"EMAIL_HOST":"<Googleアカウントのメールアドレス>",
"EMAIL_HOST_PASS":"<Googleのアプリパスワード>",
"DB_USER": "<PostgreSQLのユーザー名>",
"DB_PASSWORD": "<PostgreSQLのパスワード>"
}
下記の記事を参考にデータベースの設定を行います(既にお済みの方は不要です)。
ターミナルでプロジェクトのルートディレクトリに移動し、下記のコマンドによりローカルサーバーを立ち上げましょう。
(venv)$ python3 manage.py runserver
その後 http://127.0.0.1:8000 にアクセスします。
このような画面が表示されればOKです!
何を実装するか
今回はDetailViewという汎用ビューを使用して、テーブルデータの1レコード分のデータを使用して個別詳細ページを作成する方法をご紹介いたします。
具体的な流れはこんな感じです。
このようにメニュー一覧ページにて、メニューをクリックすると個別の商品詳細ページに遷移しています。
※反対に、データベースからデータをリストとして抽出し、描写するListViewの使い方についてはこちらをご覧ください。
それでは実際に実装する流れを解説していきます!
個別詳細機能を実装する
DetailViewを使用して個別詳細ページを実装するまでに作成・編集するファイル群は下記の通りです。
ファイル名 | 役割 |
---|---|
urls.py | ルーティング |
views.py | DetaiViewを用いて1レコード情報を取得し、テンプレートに渡す |
menu_detail.html | views.pyから受け取ったデータを描写 |
それでは1つずつ編集していきましょう!
ルーティングの設定
まずルーティング設定を行います。アプリケーション内の urls.py を下記のように編集します。
from django.urls import path
from . import views
app_name = 'cafe_app'
urlpatterns = [
# ... 略 ...
path('menu-detail/<int:pk>/', views.MenuDetailView.as_view(), name="menu_detail"),
]
冒頭でも述べましたが、今回は1レコード分のみの情報を特定して取得し、描写する必要があります。したがって、URLに対象のレコードを特定できるような情報を含め、紐づける必要があります。
もう少し噛み砕いて話すと、「https://<ホスト名>/menu-detail」のみでは「どのレコード情報??」となってしまいますので、「https://<ホスト名>/menu-detail/<int:pk>/」としてレコードを特定するような情報を含めています。
「pk」はDjangoでデフォルトでモデルの主キーを表しています。学校における出席番号のようなもので、あるデータベース(学校)内のテーブル(教室)から主キー(出席番号)を用いて1レコード(生徒)を特定することができるものです。
ちなみにこの主キーの名前「pk」は変更することができます。
さらにレコードを特定する際は、主キーではなく任意のモデルのフィールドを用いることもできます(上図でいうとこの name, price, description)。たとえば「https://<ホスト名>/menu-detail/紅茶/」といった具合です。その方法については後述します。
ビューの作成
1レコード分のデータを描写するために DetailView を使用してビューを作成していきます。アプリケーションディレクトリ内の views.py に下記のようにビューを新規作成しましょう。
# ... 略 ...
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import CafeMenu
class MenuDetailView(LoginRequiredMixin, generic.DetailView):
model = CafeMenu
template_name = 'menu_detail.html'
日記詳細ページはログインしたユーザーのみアクセスできるように LoginRequiredMixinクラスを継承しています。さらに何度もでてきていますが、1レコード分のデータを描写するために DetailViewクラスを継承しています。
それらを継承して新規作成した MenuDetailView 内では、「取得するテーブルを定義しているモデル」と「描写するテンプレート」をしています。これだけで、主キー(pk)を用いた特定を行って描写してくれます。
(参考)models.py
from accounts.models import CustomUser
from django.db import models
class CafeMenu(models.Model):
"""カフェのメニューのモデル"""
# カスタムユーザーモデルから引っ張ってくる.
user = models.ForeignKey(CustomUser, verbose_name='ユーザー', on_delete=models.PROTECT)
photo = models.ImageField(verbose_name='写真', blank=True, null=True)
name = models.CharField(verbose_name='商品名', max_length=255, default='商品名')
price = models.PositiveIntegerField(verbose_name='値段')
description = models.CharField(verbose_name='説明', max_length=255)
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)
class Meta:
# 管理画面での表示名を指定。
verbose_name_plural = 'Cafe_App'
# 管理画面でレコードを判明できるように表示されるものを設定.
def __str__(self):
return self.name
テンプレートの作成
先ほど作成した MenuDetailView から受け取ったデータを使用して描写を行うテンプレートを作成しましょう。
「cafe_app/templates/」ディレクトリに menu_detail.html を新規作成し、下記のように修正しましょう。
{% extends 'base.html' %}
{% load static %}
{% block title %}メニュー詳細{% endblock title %}
{% block contents %}
<section class="sectionMenu">
<div class="wrapperHeader">
<h2>MENU DETAIL</h2>
</div>
<div class="wrapperMenu">
<ul class="ulMenu">
<li class="menuContent">
{% if object.photo %}
<img src="{{ object.photo.url }}" alt="{{ object.name }}">
{% endif %}
<h3>{{ object.name }}</h3>
<p class="price">{{ object.price }}円</p>
<p class="description">{{ object.description }}</p>
<hr>
<p class="created_at">作成日時:{{ object.created_at }}</p>
<p class="updated_at">更新日時:{{ object.updated_at }}</p>
</li>
</ul>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
DetailViewビューからは1つのオブジェクトデータしか渡されず、{{ object }} で取り出すことができます。
for文でループさせる必要はありません。
また {% if object.photo %} と記述することで、画像データがあれば写真を表示させるようにしています。
ついでにCSSにも下記を追記しましょう。
.menuContent .created_at, .menuContent .updated_at {
display: block;
margin-top: 5px;
}
.menuContent hr {
margin: 30px 0;
}
リンクを作成する
最後に、オリジナルメニュー一覧ページ(ListViewを用いて、テーブルデータの一覧を表示しているページ)にて、各メニューに対して詳細ページへのリンクを貼りましょう。
「cafe_app/templates/」ディレクトリ内の original_menu_list.html を下記のように編集してください。
{% extends 'base.html' %}
{% load static %}
{% block title %}オリジナルメニュー{% endblock title %}
{% block contents %}
<section class="sectionMenu">
<div class="wrapperHeader">
<h2>ORIGINAL MENU</h2>
<p class="notion"><a href="{% url 'cafe_app:create_menu' %}">新規メニューを作る</a></p>
</div>
<div class="wrapperMenu">
<ul class="ulMenu">
{% for menu in object_list %}
<a href="{% url 'cafe_app:menu_detail' menu.pk %}">
<li class="menuContent">
<img src="{{ menu.photo.url }}" alt="{{ menu.name }}">
<h3>{{ menu.name }}</h3>
<p class="price">{{ menu.price }}円</p>
<p class="description">{{ menu.description }}</p>
</li>
</a>
{% empty %}
<p>メニューがありません</p>
{% endfor %}
</ul>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
ここでポイントなのは16行目の<a>タグです。urlタグはルーティングの後ろに引数を追加することができます。
<a href="{% url 'cafe_app:menu_detail' menu.pk %}">
このように記述することで「~/menu-detail/<menu.pk>/」というURLを生成してくれます。
このようにリンクを作成できたら、メニュー一覧ページにてメニューを選択することで、詳細ページへ遷移することができます。
無事詳細ページを作成することができました!
主キーの変数名を「pk」→「id」に変える
djangoデフォルトの主キーを表す「pk」を「id」に変更したい場合の記述方法をご紹介いたします。
class MenuDetailView(LoginRequiredMixin, generic.DetailView):
model = CafeMenu
slug_url_kwarg = 'id'
template_name = 'menu_detail.html'
ビューにて slug_url_kwag = ‘id’ を追加します。
商品名を用いてレコードを特定する
ここまで、商品ごとに一意に定められる主キーを用いてレコードの特定を行なっておりました。
代わりに、任意のモデルフィールドを使用して特定する方法をご紹介いたします(今回は「商品名」を用いていきます)。
ルーティング
from django.urls import path
from . import views
app_name = 'cafe_app'
urlpatterns = [
# ... 略 ...
path('menu-detail/<str:name>/', views.MenuDetailView.as_view(), name="menu_detail"),
]
ビュー
# ... 略 ...
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import CafeMenu
class MenuDetailView(LoginRequiredMixin, generic.DetailView):
model = CafeMenu
slug_field = 'name' # モデルのフィールド名
slug_url_kwarg = 'name' # urls.pyでのキーワード名
template_name = 'menu_detail.html'
リンク
<a href="{% url 'cafe_app:menu_detail' menu.name %}">
以上のように書き換えることで、レコードの特定に「商品名」が利用されます。
このようにキーワードに商品名が使用されています。
ちなみに、同じ値のものがデータベースに複数ある場合はエラーが発生してしまいます。なので一意なキーである値のみを使用しましょう。
特別な理由がない限り「pk」でいいかと思います。
お疲れ様でした!
この時点でのコード全体をGithubに載せております。確認用としてお使いいただければと思います。
まとめ
本セクションでは、DetailViewを用いて個別詳細ページを作成する方法を紹介いたしました。次回以降は、本セクションで作成した詳細ページをもとに、編集や削除機能を追加していきます。
ご覧いただきありがとうございました!
コメント
コメント一覧 (5件)
[…] (発展編 #06)DetailViewを用いて詳細表示 […]
[…] (発展編 #06)DetailViewを用いて詳細表示 […]
[…] (発展編 #06)DetailViewを用いて詳細表示 […]
[…] (発展編 #06)DetailViewを用いて詳細表示 […]
[…] (発展編 #06)DetailViewを用いて詳細表示 […]