本記事では「初めてのDjango(発展編)」として、初めてDjangoというフレームワークを使用してWebアプリケーションを作ってみたい!という方向けに、簡単なアプリケーションを作成しながら基本的な知識をご紹介していきます。
本シリーズの全容はこのようになっております。
- (入門編 #01)単一ページのアプリケーション作成まで
- (入門編 #02)CSSの適用からベーステンプレートの作成まで
- (入門編 #03)コンテキスト情報とテンプレートタグの活用
- (入門編 #04)お問い合わせフォームの設置、メール送信処理まで
- (入門編 #05)お問い合わせ送信後にメッセージを表示させる
- (入門編 #06)アプリケーションをGAEにデプロイ
- (発展編 #01)データベースの設定を行う
- (発展編 #02)カスタムユーザーモデルの定義、マイグレーション
- (発展編 #03)django-allauthで認証機能を実装する
- (発展編 #04)ListViewを用いてモデル情報を描写する
- (発展編 #05)CreateViewを使用してデータベース更新 ←イマココ
- (発展編 #06)DetailViewwを用いて詳細表示
- (発展編 #07)UpdateViewを用いてデータベース編集
- (発展編 #08)DeleteViewを用いてデータベースのレコード削除
・CreateViewクラスの使い方
・Webアプリケーション上でデータベースを更新する
こちら(Github)のコードの続きとして作成しますので、本記事単体でご覧になる方はぜひご参考にしてください。
本記事単体でご覧になる方へ
本記事は連作となっており、こちら(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です!
何を実装するか
今回は、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を使用する場合は、
- 紐づけるモデル
- 紐づけるフォーム
が必要になってきます。
モデルは、登録したいメニューのモデルである 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を用いてメニュー詳細ページを作成する方法を解説します。
ご覧いただきありがとうございました!
コメント
コメント一覧 (3件)
[…] (発展編 #05)CreateViewを使用してデータベースを更新 […]
[…] (発展編 #05)CreateViewを使用してデータベースを更新 […]
[…] (発展編 #05)CreateViewを使用してデータベースを更新 […]