【初めてのDjango】(発展編 #05)CreateViewを使用してデータベース更新

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

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

今回の内容

・CreateViewクラスの使い方
・Webアプリケーション上でデータベースを更新する

こちら(Github)のコードの続きとして作成しますので、本記事単体でご覧になる方はぜひご参考にしてください。

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

本記事は連作となっており、こちら(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
管理サイトから商品メニューデータを登録する

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

目次

何を実装するか

今回は、Djangoのアプリケーション上で入力したデータをデータベースに登録する方法をご紹介していきます。

前回記事にて、「データベースの情報をテンプレートに渡し、描写する方法」をご紹介しましたが、データ自体は管理サイトで登録しておりました。

本記事の内容を実装することで、管理サイトを使わずにWebアプリケーション上のみでデータの登録を行うことができます。

実装後のイメージはこんな感じです。

それでは実装していきましょう!

メニュー作成機能を作る

ルーティングの追加

まずは新規作成ページのルーティングの追加を行います。「cafe_app/」内の urls.py に下記を追加しましょう。

from django.urls import path
from . import views

urlpatterns = [
    # ... 略 ...
    path('create-menu', views.CreateMenuView.as_view(), name="create_menu"), 
]

https://<ホスト名>/create-menu」にアクセスがあった際に、CreateMenuViewに処理を渡すようにしています。

ビューの作成

続いて、新規メニュー作成ページ用のビューを追加します。

今回はデータの登録や作成するときに用いられる CreateView というビューを使用します。CreateView を使用することで、まどろっこしい処理を記述することなく簡単にデータ保存フォームを作成することができます。

CreateViewを使用する場合は、

  1. 紐づけるモデル
  2. 紐づけるフォーム

が必要になってきます。

モデルは、登録したいメニューのモデルである CafeMenu を使用します(前回記事で作成しました)。

メニューのモデル(models.CafeMenu)
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

またフォームに必要な項目もモデル(CafeMenu)と大部分が重複していますので、CafeMenuを継承して作成していきます(後述)。

これらのことを踏まえて、views.py を下記のように編集しましょう。

import logging
from django.urls import reverse_lazy
from django.views import generic
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin

from .forms import ContactForm, CreateMenuForm
from .models import CafeMenu

# ... 略 ...

class CreateMenuView(LoginRequiredMixin, generic.CreateView):
    model = CafeMenu
    template_name = 'create_menu.html'
    form_class = CreateMenuForm

    # 処理が成功したときのリダイレクト先の設定
    success_url = reverse_lazy('cafe_app:original_menu_list')

    # データベースに保存するだけなら不要だが、userやcreated_atなどがカスタムユーザーモデルからなのでオーバーライドする必要がある.
    def form_valid(self, form):
        menu = form.save(commit=False)
        menu.user = self.request.user
        menu.save()
        messages.success(self.request, '新規メニューを作成しました')
        return super().form_valid(form)

新規作成ページはログインしたユーザーのみがアクセスできるように、「LoginRequiredMixin」クラスも継承しています。

また、先ほど CreateView に必要だと説明したモデルとフォームも指定しています。

model = CafeMenu
form_class = CreateMenuForm

このビューで一番重要なのが、form_valid()メソッドです。これはフォームの内容に問題がなければ実行されるメソッドなのですが、わざわざオーバーライドして記述しなくてもデータベースへの保存を行なってくれます

ではなぜ今回はこのようにオーバーライドしているのでしょうか?

改めて、CafeMenuモデルで定義している項目を列挙してみます。

  • ユーザー(作成者)
  • 写真
  • 商品名
  • 値段
  • 説明
  • 作成日時
  • 更新日時

このなかでユーザーがフォーム入力するのは下記の項目です。

  • 写真
  • 商品名
  • 値段
  • 説明

では残りのユーザー・作成日時・更新日時はどこから取得するのでしょうか?改めてモデルの定義部分を見てみます。

user = models.ForeignKey(CustomUser, verbose_name='ユーザー', on_delete=models.PROTECT)
created_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)

ユーザーはカスタムユーザーモデルから紐づけているので、フォームで入力する必要はありません。また作成日時・更新日時は、フィールドオプションからも分かるように自動で付与されます

したがって、フォーム入力だけでは足りない項目(ユーザー)を、form_valid()メソッド内で紐づけているのです。

def form_valid(self, form):
    menu = form.save(commit=False)
    menu.user = self.request.user
    menu.save()
    messages.success(self.request, '新規メニューを作成しました')
    return super().form_valid(form)

これを記述しないと「データベースに保存したいけどユーザーの情報ないやん」状態でエラーが発生します。

また「menu.save()」によって実際にデータベースへの保存を実行しています。

これでビューは完成しましたので、CreateViewに必要なフォームを作成していきます。

フォームの作成

forms.py を下記のように編集してください。

# ... 略 ...
from .models import CafeMenu

# ... 略 ...

class CreateMenuForm(forms.ModelForm):
    class Meta:
        model = CafeMenu
        fields = ('photo', 'name', 'price', 'description')

先ほども述べましたが、今回のフォームは項目の大部分が重複するCafeMenuモデルを継承して作成しています。さらに継承する項目を「fields」の中にタプルとして格納しています。

ユーザーはカスタムユーザーから、作成日時・更新日時は自動付与なので継承していません。

テンプレートの作成

準備が整いましたので、メニューを新規作成するページのテンプレートを作成します。「cafe_app/templates/」ディレクトリ直下に create_menu.html を新規作成し、下記のように修正してください。

{% extends 'base.html' %}

{% load static %}

{% block title %}お問い合わせ{% endblock title %}

{% block contents %}
<section class="sectionContact">
    <div class="wrapperHeader">
        <h2>CREATE</h2>
    </div>
    <div class="wrapperForm">
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}

            {{ form.non_field_errors }}

            {% for field in form %}
            <div class="formContent">
                <p>{{ field.label_tag }}</p>
                {{ field }}
                {{ field.errors }}
            </div>
            {% endfor %}
            <button type="submit">作 成</button>
        </form>
    </div>
    <a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}

ポイントは {% for field in form %} によってフォームをひとつずつ取り出している点です。今回はCSSの関係上このように記述していますが、下記のように記述することでループせずとも描写することができます。

{{ form.as_table }}

また、今回はファイルのアップロードが必要なフォームなので、<form>タグに「enctype=”multipart/form-data”」という属性を与える必要がります。

<form method="post" enctype="multipart/form-data">

リンクの作成

最後に、メニュー作成画面へのリンクを作成します。今回はオリジナルメニュー一覧ページより遷移できるようにします。original_menu_list.html に下記の一行を追加します。

<!-- ... 略 ... -->

<section class="sectionMenu">
    <div class="wrapperHeader">
        <h2>ORIGINAL MENU</h2>
        <p class="notion"><a href="{% url 'cafe_app:create_menu' %}">新規メニューを作る</a></p>
    </div>

    <!-- ... 略 ... -->

</section>

ついでにCSSファイルにも下記を追加してください。

.notion {
    display: block;
    margin: 20px;
}

リンクを貼る場所もぶっちゃけどこでも構いませんので、お好きな場所に作成してください。

この状態で、管理サイトではなくメニュー新規作成画面からメニューを追加し、一覧に表示されれば万事OKです!

お疲れ様でした!

この時点でのコード全体をGithubに載せております。確認用としてお使いいただければと思います。

まとめ

本セクションでは、CreateViewクラスを用いてDjangoのWebアプリケーション上でデータベースを更新する方法についてご紹介しました。1つのモデルに対して保存を行う場合はよく用いますので、ぜひマスターしましょう!

次回は、ListViewで作成したメニュー一覧ページから、DetailViewを用いてメニュー詳細ページを作成する方法を解説します。

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

案件、ありますか?

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

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

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

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

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

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

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

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

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

この記事を書いた人

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

コメント

コメント一覧 (3件)

コメントする

目次