• トップ
  • ブログ一覧
  • 【第3回】Djangoで日記アプリを作ろう~投稿フォーム編~
  • 【第3回】Djangoで日記アプリを作ろう~投稿フォーム編~

    メディアチームメディアチーム
    2021.04.30

    エンジニアになろう!

    【第3回】Djangoで日記アプリを作ろう~投稿フォーム編~

    【第3回】Djangoで日記アプリを作ろう ~投稿フォーム編~

    前回は、TemplateView を利用して、日記アプリのトップページを作成しました。

    【第2回】Djangoで日記アプリを作ろう~トップ画面編~2021.04.23【第2回】Djangoで日記アプリを作ろう~トップ画面編~【第2回】Djangoで日記アプリを作ろう~トップ画面編~前回は、Django のアーキテクチャの全体像を学び、日記ア...

    今回は日記投稿フォーム画面を作成し、さらにトップページに投稿画面へのリンクを貼りましょう!

    この投稿フォームの作成を通して、下記の内容を学びます。

    1. Mode lクラスを利用したモデルの実装方法
    2. Model Form クラスを用いたフォームの実装方法
    3. CreateView を用いたビューの実装方法
    4. テンプレート内部に url を記述する方法
    5. マイグレーションの実行方法

    フォームとモデルの概念

    まずは、フォームとモデルという概念を整理しましょう!

    フォーム

    フォームは、ユーザーに対して何かしらの入力を求める場面で、登場する概念です。

    なので、フォームの実装では、「ユーザーからどのようなデータを受け取るか?」という定義を行います。

    モデル

    一方、モデルは Django アプリとデータベースとのやりとりで登場する概念です。

    よって、モデルの実装では「データベースにおけるテーブルのカラム情報」を記載します。

    フォーム経由でユーザーからデータを受け取り、必要であればビューでそのデータに追加情報を加えます。

    その後、「テーブルに書き込む」というのが、基本的なフォームデータの流れです。

    Django ORM マッパー

    Django アプリとデータベースのやりとりを支える機能が、Django ORM マッパーです。

    この機能により、開発者はモデルをPythonクラスとして実装し、その後「マイグレーション」という操作を実行するだけで、接続しているデータベースにテーブルを作成することができます。

    クエリを用いて、データベース上にテーブルを作成する必要はありません。

    Djangoにおけるデータベースの設定

    次に、データベースの設定をしていきましょう。

    とは言ったものの、本連載の範囲ではデータベースの設定は特に必要ありません

    実は Django プロジェクト作成時に、SQLite というデータベースとの接続設定が、既に行われています。

    データベース自体も、後ほど説明する「makemigrations」コマンドにより、自動で作成されます。

    SQLite は、Python に標準的に備わっている、簡易的なデータベースです。

    db.sqlite3 というファイルで作成されるため、削除が簡単でやり直しが効きやすく開発時には便利です。

    本連載では、SQLite を利用していきます。

    SQLite との接続設定を確認してみましょう!

    config ディレクトリ内部の「setting.py」の中から、以下のような箇所を探してみて下さい。

    1DATABASES = {
    2    'default': {
    3        'ENGINE': 'django.db.backends.sqlite3',
    4        'NAME': BASE_DIR / 'db.sqlite3',
    5    }
    6}

    ENGINE キーに対して、SQLite3 用のデータベースバックエンドが指定されています。

    Django が標準で提供する、他のデータベースバックエンドについては、以下のリンク先を参照ください。

    【Django ドキュメント:設定】
    https://docs.djangoproject.com/ja/3.1/ref/settings/#std:setting-DATABASE-ENGINE

    日記投稿フォームを作ろう!

    では、日記の投稿フォームを作っていきましょう。

    所々で、設定ファイルの編集が必要となりますので、お忘れなく!

    Modelの実装

    今回は、以下のような定義の日記テーブルを作成することにします。

    カラム名意味
    idUUIDデータのid
    dateDate日付
    titleChar日付のタイトル
    textChar日付の本文
    created_atDateTime作成日時
    updated_atDateTime編集日時

    (updated_at は、今後データの編集を行うことを見据えて、ここで定義しておきます)

    Django では、上記のテーブル定義を元に、SQL でテーブルを作りません。

    モデルを実装することで、自動でテーブルを作成します。

    diary ディレクトリ内の models.py に、以下のコードを記述しましょう。

    1from django.db import models
    2from django.utils import timezone
    3import uuid
    4
    5
    6class Diary(models.Model):
    7    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    8    date = models.DateField(verbose_name='日付', default=timezone.now)
    9    title = models.CharField(verbose_name='タイトル', max_length=40)
    10    text = models.CharField(verbose_name='本文', max_length=200)
    11    created_at = models.DateTimeField(verbose_name='作成日時', default=timezone.now)
    12    updated_at = models.DateTimeField(verbose_name='編集日時', blank=True, null=True)

    Django の models モジュールにある、Model クラスを継承した Diary クラスを作成しています。

    テーブルのカラムが、「UUIDField」や「DateField」といった、Django のクラスを利用して定義されています。

    id に関しては、model 上で定義をしなくても、自動で id カラムは作成されます。

    ただし、データを作成する度に、連番で id が付与される仕様となっています。

    今回は id を UUID とするために、あえて UUIDField を用い、 id フィールドを上書きしています。

    各 Field クラスは、default や verbose_name など、様々なフィールドを持っています。

    詳細は、以下のリンク先を参照してください。

    【Django ドキュメント:Model field referenc】
    https://docs.djangoproject.com/en/3.1/ref/models/fields/

    LANGUAGE CODE と TIMEZONEの変更

    Django デフォルトの言語コードは「en-us」、つまり英語になっています。

    このままだと、日付型において11月が「Nov.」のように、英語に翻訳されてしまいます。

    config ディレクトリの settings.py の中に、 LANGUAGE_CODE = 'en-us' という記述を探し、ja-jp」に書き換えましょう。

    また、Django のデフォルトの TIMEZONE は、協定世界時(UTC)となっています。

    つまり現状では、Diary モデルの created_at の default 値に、UTC 基準の時間が入ります。

    これも、日本時間に変更しましょう。

    config ディレクトリの settings.py の中に、 TIME_ZONE = 'UTC'  という記述があります。

    これを、 TIME_ZONE = 'Asia/Tokyo' に変更します。

    これで日本時間を扱えるようになります。

    Formの実装

    続いて、フォームの実装をしていきましょう!

    実装方法が2種類ある

    Django では、Form の実装方法が2種類あります。

    Form クラスを利用する場合と、ModelForm クラスを利用する場合です。

    Form クラスの実装スタイルは、Model の実装と類似しており、フォームの値と型を1つ1つ記述していきます。

    一方 ModelForm クラスの実装スタイルは、Model 定義を再利用し「どのカラムをフォーム値として利用するか?」という記述をしていきます。

    Model を定義した状況では、ModelForm クラスを利用した方が、コーディングの量が少なくなります。

    なので、本連載でも ModelForm を利用していきます。

    フォームを実装してみよう

    では、ModelForm を利用してフォームを実装しましょう。

    今回の日記アプリでは、ユーザーから「日付」「日記のタイトル」、そして「日記の本文」のデータを受け取るとします。

    Diary ディレクトリ内に、forms.py ファイルを作成して、以下のコードを記述しましょう!

    ちなみに forms.py は、startapp コマンド実行時には作成されません

    1from django import forms
    2from .models import Diary
    3
    4
    5class DiaryForm(forms.ModelForm):
    6    class Meta:
    7        model = Diary
    8        fields = ('date', 'title', 'text',)

    forms モジュールの ModelForm クラスを継承した DiaryForm を作成していますね。

    Diary を model というフィールド変数に設定し、実際に入力フォームで利用するフィールドを、fields 変数にタプル形式で与えています。

    fields は、入力フォームで利用するカラム名を渡しますが、逆にフォームで利用しないカラム名を渡すことも可能です。

    それが、exclude フィールドです。

    例えば、今回の DiaryForm の例だと、

    1exclude = ('id', 'created_at', 'upadted_at',)

    と記述しても OK です。

    Viewの実装

    次に、日記の投稿画面に対応するビューを実装しましょう!

    フォームを伴った画面作成用に、便利なビュークラスがあります。

    それが CreateView です。

    以下のコードを、diary ディレクトリの views.py に追記しましょう。

    1class DiaryCreateView(CreateView):
    2    template_name = 'diary_create.html'
    3    form_class = DiaryForm
    4    success_url = reverse_lazy('diary:diary_create_complete')

    DiaryCreateView には、3つのフィールドが定義されていますね!

    template_name は、DiaryCreateView に紐づくテンプレートを表しています(diary_create.html は後ほど作成します)。

    form_class というフィールド変数は重要です。

    この変数に DiaryForm を渡すことによって、diary_create.html 内部で、DiaryForm を用いた入力フォームを作成することができます。

    success_url には、入力フォームを投稿した後に、遷移する url を指定します。

    一見すると、diary アプリの「diary_create_complete」という名前のビューを呼び出しているようですが、reverse_lazy() という見慣れないメソッドが使われています。

    reverse_lazy() は、url を遅延評価するメソッドです。

    「diary:diary_create_complete」という書き方は、urls.py の情報を利用した url の指定方法です。

    Django プロジェクト起動の際には、urls.py の評価よりも、ビュークラスの評価が先に行われます。

    ですので、reverse_lazy で url の遅延評価を行わなければ url の解決ができず、プロジェクトの起動ができません

    最後に、投稿ボタンを押した後の画面に対応するビューを、TemplateView を用いて実装しましょう。

    投稿後に表示するテンプレートは、「diary_create_complete.html」とします。

    1class DiaryCreateCompleteView(TemplateView):
    2    template_name = 'diary_create_complete.html'

    第1回、第2回と続けて見て下さっている方は、第2回に作成した IndexView を含めて、合計3つのビューが実装されているはずです。

    テンプレートの作成

    では、テンプレートの作成に移りましょう!

    日記投稿画面と、日記投稿完了画面の2つのテンプレートを作成します。

    日記投稿画面は、以下のようなテンプレートとします。

    テンプレート名は、「diary_create.html」としましょう!

    1<!DOCTYPE html>
    2<html lang="en">
    3<head>
    4    <meta charset="UTF-8">
    5    <title>MyDiaryApp</title>
    6</head>
    7<body>
    8
    9<form method="post">
    10    {% csrf_token %}
    11    {{ form.as_p }}
    12    <button type="submit">投稿</button>
    13</form>
    14
    15</body>
    16</html>

    post リクエストを送信している form タグに着目をして下さい。

    action 属性が設定されていません。

    DiaryCreateView の success_url 属性に指定した url に、自動で action を起こします

    また、form タグの中には、他にも見慣れない記述が2つあります。

    {% csrf_token %}という記述

    1つ目が {% csrf_token %}  という記述です。

    これは Django において、POST リクエストを送信するときには必須の記述で、クロスサイトリクエストフォージェリを防ぐ仕組みです。

    なお、{ } という記法は、Django テンプレート言語と呼ばれているものです(今後の記事で詳しく説明します)。

    {{ form.as_p }}という記述

    もう1つ、{{ form.as_p }}という記述があります。

    DiaryCreateView の form_class 属性と結びついており、form は DiaryForm のインスタンスを意味しています。

    また、as_p という記述は、form の各要素を p タグで囲むという意味になります。

    続いて投稿完了画面を作りましょう。

    以下のようなテンプレートを作成して下さい。

    テンプレート名は、「diary_create_complete.html」とします。

    1<!DOCTYPE html>
    2<html lang="en">
    3<head>
    4    <meta charset="UTF-8">
    5    <title>MyDiaryApp</title>
    6</head>
    7<body>
    8<p>日記の投稿が完了しました!</p>
    9<a href="{% url 'diary:index' %}">トップへ戻る</a>
    10</body>
    11</html>

    このテンプレート内の a タグに着目してください。

    href がしている url が特徴的です。

    これも、Django テンプレート言語の書き方です。

    今回の記述だと、「diary アプリの index という名前のビューに対応する url」が、最終的に埋め込まれます。

    つまり、トップページへの url です。

    urls.pyの編集

    ビューを2つ作成したので、urls.py を編集します。

    diary ディレクトリ内の urls.py に対して、以下のような追記を行いましょう。

    1from django.urls import path
    2from . import views
    3
    4app_name = 'diary'
    5urlpatterns = [
    6    path('index/', views.IndexView.as_view(), name='index'),
    7    path('diary/create/', views.DiaryCreateView.as_view(), name='diary_create'),
    8    path('diary/create/complete/', views.DiaryCreateCompleteView.as_view(), name='diary_create_complete'),
    9]

    上記では、以下を記述しています。

    1. 日記の投稿
    2. 投稿の完了に対応する url
    3. ビュー
    4. ビューの逆引き名

    マイグレーションの実施

    マイグレーションとは、データベースの定義を自動で行うことです。

    Django では、model.py に定義されたモデルを利用して、データベース内部のテーブルを自動作成します。

    以下のコマンドを実行してみましょう。

    1python3 manage.py makemigrations

    コマンドライン上に、以下のような出力が発生したと思います。

    1Migrations for 'diary':
    2  diary/migrations/0001_initial.py
    3    - Create model Diary

    上記コマンドを実行すると、まず mydiaryproject 直下に、「db.sqlite3」というファイルができています。

    これが SQLite です。

    またdiaryアプリ内に、「migrations ディレクトリ」が新規で作成されていると思います。

    その中に、「0001_initial.py」というファイルができています。

    これが、マイグレーションファイルと呼ばれているもので、テーブル定義に関する情報を持っています

    マイグレーションファイルの中身

    マイグレーションファイルの中身をのぞいてみましょう。

    1# Generated by Django 3.1.2 on 2020-11-13 15:10
    2
    3from django.db import migrations, models
    4import django.utils.timezone
    5import uuid
    6
    7
    8class Migration(migrations.Migration):
    9
    10    initial = True
    11
    12    dependencies = [
    13    ]
    14
    15    operations = [
    16        migrations.CreateModel(
    17            name='Diary',
    18            fields=[
    19                ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
    20                ('date', models.DateField(default=django.utils.timezone.now, verbose_name='日付')),
    21                ('title', models.CharField(max_length=40, verbose_name='タイトル')),
    22                ('text', models.CharField(max_length=200, verbose_name='本文')),
    23                ('created_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='作成日時')),
    24                ('update_at', models.DateTimeField(blank=True, null=True, verbose_name='編集日時')),
    25            ],
    26        ),
    27    ]

    models.py の Diary モデルと同じような情報が並び、これだけだとファイルの存在意義が分かりにくいですね…。

    ですが models.pyとは別に、テーブル用のファイルが存在することで、データベースのロールバックが可能になります

    0001というように番号が付与されているのは、そのようなデータベースのヒストリーの保存のためです。

    では、このマイグレーションファイルを元に、マイグレーション処理を実行していきたいと思います。

    設定ファイルの編集

    その前に、1つだけ設定ファイルに編集を加えます。

    config ディレクトリ内部の setting.py より、下記のような箇所を見つけて下さい。

    1INSTALLED_APPS = [
    2    'django.contrib.admin',
    3    'django.contrib.auth',
    4    'django.contrib.contenttypes',
    5    'django.contrib.sessions',
    6    'django.contrib.messages',
    7    'django.contrib.staticfiles',
    8]

    INSTALLED_APPS は、マイグレーションを実行するときに、参照されるリストです。

    データベースを必要とするアプリケーションは、上記リストにアプリ名を記載します。

    上記リストに、diary を追加しましょう

    1INSTALLED_APPS = [
    2    〜省略〜
    3    'django.contrib.staticfiles',
    4    'diary', # 追加
    5]

    いよいよマイグレーションを実行!

    では、マイグレーションを実行する、下記のコマンドを実行して下さい。

    1python3 manage.py migrate

    以下のような出力が発生したと思います。

    1Operations to perform:
    2  Apply all migrations: admin, auth, contenttypes, diary, sessions
    3Running migrations:
    4  Applying contenttypes.0001_initial... OK
    5  Applying auth.0001_initial... OK
    6  Applying admin.0001_initial... OK
    7  Applying admin.0002_logentry_remove_auto_add... OK
    8  Applying admin.0003_logentry_add_action_flag_choices... OK
    9  Applying contenttypes.0002_remove_content_type_name... OK
    10  Applying auth.0002_alter_permission_name_max_length... OK
    11  Applying auth.0003_alter_user_email_max_length... OK
    12  Applying auth.0004_alter_user_username_opts... OK
    13  Applying auth.0005_alter_user_last_login_null... OK
    14  Applying auth.0006_require_contenttypes_0002... OK
    15  Applying auth.0007_alter_validators_add_error_messages... OK
    16  Applying auth.0008_alter_user_username_max_length... OK
    17  Applying auth.0009_alter_user_last_name_max_length... OK
    18  Applying auth.0010_alter_group_name_max_length... OK
    19  Applying auth.0011_update_proxy_permissions... OK
    20  Applying auth.0012_alter_user_first_name_max_length... OK
    21  Applying diary.0001_initial... OK
    22  Applying sessions.0001_initial... OK

    admin や auth など、Django プロジェクトが標準で備えているモデルに加えて、diary というモデルに関してもマイグレーション処理がなされていることが分かります。

    Django プロジェクトを起動して確認

    では、Django プロジェクトを起動しましょう!

    1python3 manage.py runserver

    「http://127.0.0.1:8000/index/ 」にアクセスすると、以下のような画面が出てくるはずです。

    「http://127.0.0.1:8000/index/ 」にアクセスすると、以下のような画面が出る

    また「日記の投稿」リンクをクリックすると、日記投稿フォーム画面に遷移します。

    実際に、日記の投稿をしてみましょう。

    投稿ボタンをクリックすると、以下のような画面に遷移します。

    投稿ボタンをクリックすると、以下のような画面に遷移

    「トップへ戻る」ボタンをクリックすると、トップ画面(index/)に戻ります。

    第4回へつづく!

    今回は、日記の投稿フォームを作成しました。

    以下の5つがポイントです。

    1. テーブル定義を行う Model クラス
    2. Model を利用したフォーム定義を行う ModelForm クラス
    3. フォームを利用したビューを簡単に実装する CreateView クラス
    4. Django テンプレート言語を用いた url の記述方法
    5. マイグレーション実行に必要なコマンド

    次回は、日記の一覧画面と詳細画面を作成していきます。

    これらの画面も、Django の便利な View クラスを利用すれば、簡単に実装できてしまいます。

    次回もお楽しみに!

    第4回はこちら!

    【第4回】Djangoで日記アプリを作ろう ~一覧画面・詳細画面編~2021.05.31【第4回】Djangoで日記アプリを作ろう ~一覧画面・詳細画面編~【第4回】Djangoで日記アプリを作ろう ~一覧画面・詳細画面編~前回は、Model と ModelForm を利用...

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
    featureImg2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background