【初めてのDjango】(発展編 #06)DetailViewを用いて詳細表示

本記事では「初めてのDjango(発展編)」として、初めてDjangoというフレームワークを使用してWebアプリケーションを作ってみたい!という方向けに、簡単なアプリケーションを作成しながら基本的な知識をご紹介していきます。

本シリーズの全容はこのようになっております。

今日の内容

・DetailViewを使用して、個別詳細ページの作成を行う

本記事単体でご覧になる方へ

本記事は連作となっており、こちら(GitHub)のコードの続きとしてご紹介させていただきます。本記事単体でもご覧いただけますが、もし上記のコードを引用して学びたい方は下記の流れに沿って準備を行なってください。

STEP
コードのダウンロード

こちらにアクセスしましょう。

「Download ZIP」をクリックし、コードをダウンロード。

STEP
settings.json

先ほどダウンロードしたファイル群を任意のディレクトリに配置します。

その後、プロジェクトのルートディレクトリ(manage.pyがあるとこ)に settings.json を新規作成し、下記の内容を記述してください。

{
    "SECRET_KEY":"<djangoのシークレットキー>",
    "EMAIL_HOST":"<Googleアカウントのメールアドレス>",
    "EMAIL_HOST_PASS":"<Googleのアプリパスワード>",
    "DB_USER": "<PostgreSQLのユーザー名>",
    "DB_PASSWORD": "<PostgreSQLのパスワード>"
}
STEP
データベース設定

下記の記事を参考にデータベースの設定を行います(既にお済みの方は不要です)。

STEP
ローカルサーバーの立ち上げ

ターミナルでプロジェクトのルートディレクトリに移動し、下記のコマンドによりローカルサーバーを立ち上げましょう。

(venv)$ python3 manage.py runserver

その後 http://127.0.0.1:8000 にアクセスします。

このような画面が表示されればOKです!

STEP
管理サイトから商品メニューデータを登録する

こちらを参考に、メニューの追加を行なってください。

目次

何を実装するか

今回はDetailViewという汎用ビューを使用して、テーブルデータの1レコード分のデータを使用して個別詳細ページを作成する方法をご紹介いたします。

具体的な流れはこんな感じです。

このようにメニュー一覧ページにて、メニューをクリックすると個別の商品詳細ページに遷移しています。

※反対に、データベースからデータをリストとして抽出し、描写するListViewの使い方についてはこちらをご覧ください。

それでは実際に実装する流れを解説していきます!

個別詳細機能を実装する

DetailViewを使用して個別詳細ページを実装するまでに作成・編集するファイル群は下記の通りです。

ファイル名役割
urls.pyルーティング
views.pyDetaiViewを用いて1レコード情報を取得し、テンプレートに渡す
menu_detail.htmlviews.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を用いて個別詳細ページを作成する方法を紹介いたしました。次回以降は、本セクションで作成した詳細ページをもとに、編集や削除機能を追加していきます。

ご覧いただきありがとうございました!

案件、ありますか?

「メインの仕事があるけれど、週1、2日だけできる仕事ないかな、、、」

「ある程度スキルが身に付いてはきたけど、そのスキルを活用できる場が欲しい」

なんて悩みが以前はありました。

自分で仕事を探しに行くのも大事ですが、蛇の道は蛇。その道の人に頼むことで、自分だけでは見つからないような案件に携わることができます。

IT PRO パートナーズでは、簡単に無料でアカウントを登録でき、さらにはエージェントさんに希望の働き方・案件の種類を提示することでお仕事を紹介してくれます!

登録自体も非常に簡単で、「エージェントさんとの面談を希望する」という欄にチェックをするだけで、エージェントさんから直接連絡をいただくことができます。

驚くほど簡単で正直拍子抜けしてしまいました笑

もしお仕事探しに困っておりましたら、一度登録し案件を眺めてみることをおすすめします!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

勤めていた設計会社を退社し、フリーランスとして活動しています
また、趣味で主にpyhonを用いて機械学習を行なっています!
現在競艇の予測モデルの開発中です。

コメント

コメント一覧 (5件)

コメントする

目次