自分だけのクイズを作成しよう - Quipha
スポンサーリンク

【Django】FullCalendarでスケジュールのDB登録・表示【実践向け】

Django

当サイトではアフィリエイト広告を利用しています。

イベントのDB登録機能の実装

ビューの修正

イベントを登録するための処理を追加します。
リクエストのバリデーションと、DBへの登録処理を追加しました。
詳しくは、ソース中のコメントをご確認ください。

scheduleCalendar/views.py
import json
from .models import Event
from .forms import EventForm
from django.http import Http404
import time
from django.template import loader
from django.http import HttpResponse

...

def add_event(request):
    """
    イベント登録
    """

    if request.method == "GET":
        # GETは対応しない
        raise Http404()

    # JSONの解析
    datas = json.loads(request.body)

    # バリデーション
    eventForm = EventForm(datas)
    if eventForm.is_valid() == False:
        # バリデーションエラー
        raise Http404()

    # リクエストの取得
    start_date = datas["start_date"]
    end_date = datas["end_date"]
    event_name = datas["event_name"]

    # 日付に変換。JavaScriptのタイムスタンプはミリ秒なので秒に変換
    formatted_start_date = time.strftime(
        "%Y-%m-%d", time.localtime(start_date / 1000))
    formatted_end_date = time.strftime(
        "%Y-%m-%d", time.localtime(end_date / 1000))

    # 登録処理
    event = Event(
        event_name=str(event_name),
        start_date=formatted_start_date,
        end_date=formatted_end_date,
    )
    event.save()

    # 空を返却
    return HttpResponse("")

リクエストはJSON形式で受け取ります。

フォームクラスを作成します。
バリデーションルールを定義しています。

scheduleCalendar/forms.py
from django import forms


class EventForm(forms.Form):

    start_date = forms.IntegerField(required=True)
    end_date = forms.IntegerField(required=True)
    event_name = forms.CharField(required=True, max_length=32)

イベント名は32文字の文字数制限としました。

登録処理のURLを追加します。

scheduleCalendar/urls.py
from django.urls import path
from . import views

app_name = "cal"
urlpatterns = [
    path("", views.index, name="index"),
    path("add/", views.add_event, name="add_event"),
]

JavaScriptの修正

非同期通信を行うためのaxiosを追加します。
index.htmlにCDNを使用して、axiosを追加します。

scheduleCalendar/templates/
scheduleCalendar/index.html
<html>

<head>
    <meta charset='utf-8' />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.2/main.min.css">
</head>

<body>
    <div id='calendar'></div>

    {% load static %}
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.10.2/main.min.js"></script>
    <script src="{% static 'scheduleCalendar/app.js' %}"></script>
</body>

</html>

それではイベントを追加する処理に登録処理の呼び出しを実装してみましょう。
修正箇所だけ抜粋したコードです。

scheduleCalendar/static/scheduleCalendar/app.js
        select: function (info) {
            //alert("selected " + info.startStr + " to " + info.endStr);

            // 入力ダイアログ
            const eventName = prompt("イベントを入力してください");

            if (eventName) {

                // 登録処理の呼び出し
                axios
                    .post("/sc/add/", {
                        start_date: info.start.valueOf(),
                        end_date: info.end.valueOf(),
                        event_name: eventName,
                    })
                    .then(() => {
                        // イベントの追加
                        calendar.addEvent({
                            title: eventName,
                            start: info.start,
                            end: info.end,
                            allDay: true,
                        });

                    })
                    .catch(() => {
                        // バリデーションエラーなど
                        alert("登録に失敗しました");
                    });
            }
        },

CSRFの対応を追記します。
JavaScriptの先頭に追記します。

scheduleCalendar/static/scheduleCalendar/app.js
// CSRF対策
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
axios.defaults.xsrfCookieName = "csrftoken"

..

カレンダーの画面を表示した際に、CSRFのトークンを発行するようにします。
これがないと、CSRFの403エラーになります。

scheduleCalendar/views.py
from django.template import loader
from django.http import HttpResponse
from django.middleware.csrf import get_token

# Create your views here.


def index(request):
    """
    カレンダー画面
    """
    # CSRFのトークンを発行する
    get_token(request)

    template = loader.get_template("scheduleCalendar/index.html")
    return HttpResponse(template.render())

これで登録処理の流れができました。実際に試してみましょう。
以下のURLにアクセスします。

http://localhost:8000/sc/

プロンプトに任意のイベント名を入力します。

データベースに正しく登録されました。

エラー処理も確認してみましょう。
イベント名は32文字の文字数制限をかけましたので、32文字以上のイベント名を登録してみます。

失敗アラートが表示され、カレンダー上にイベントは表示されませんし、当然DBにも登録されません。

エラーメッセージは簡易的にしましたが、バリデーション結果を表示するのも良いでしょう。
今回は不正な値が登録されないように、バリデーション処理の実装を行いました。

スポンサーリンク

イベントの表示

ビューの修正

イベントの登録が完了しました。
今度はカレンダーの表示時に、データベースに登録されているイベントを表示します。

イベントを取得するための処理を追加します。
カレンダーが表示されている期間のみ、イベントを取得するようにします。

基本的にカレンダーは1ヶ月分表示されています。
表示されている期間のみのデータを返却するようにしましょう。
そうしないと、毎回全イベントを返却することになり、効率が悪いです。

scheduleCalendar/views.py
from django.http import JsonResponse
from .forms import CalendarForm

...

def get_events(request):
    """
    イベントの取得
    """

    if request.method == "GET":
        # GETは対応しない
        raise Http404()

    # JSONの解析
    datas = json.loads(request.body)

    # バリデーション
    calendarForm = CalendarForm(datas)
    if calendarForm.is_valid() == False:
        # バリデーションエラー
        raise Http404()

    # リクエストの取得
    start_date = datas["start_date"]
    end_date = datas["end_date"]

    # 日付に変換。JavaScriptのタイムスタンプはミリ秒なので秒に変換
    formatted_start_date = time.strftime(
        "%Y-%m-%d", time.localtime(start_date / 1000))
    formatted_end_date = time.strftime(
        "%Y-%m-%d", time.localtime(end_date / 1000))

    # FullCalendarの表示範囲のみ表示
    events = Event.objects.filter(
        start_date__lt=formatted_end_date, end_date__gt=formatted_start_date
    )

    # fullcalendarのため配列で返却
    list = []
    for event in events:
        list.append(
            {
                "title": event.event_name,
                "start": event.start_date,
                "end": event.end_date,
            }
        )

    return JsonResponse(list, safe=False)

フォームクラスを作成します。
バリデーションルールを定義しています。

scheduleCalendar/forms.py
from django import forms

..

class CalendarForm(forms.Form):

    start_date = forms.IntegerField(required=True)
    end_date = forms.IntegerField(required=True)

一覧取得処理のURLを追加します。

scheduleCalendar/urls.py
from django.urls import path
from . import views

app_name = "cal"
urlpatterns = [
    path("", views.index, name="index"),
    path("add/", views.add_event, name="add_event"),
    path("list/", views.get_events, name="get_events"),
]

app.jsを開き、イベントを取得する処理を記述します。

「events」プロパティを追加します。
カレンダーが表示された時や、前の月・次の月のように表示が切り替わる度に呼ばれます。

scheduleCalendar/static/scheduleCalendar/app.js
    var calendar = new FullCalendar.Calendar(calendarEl, {
        initialView: 'dayGridMonth',

...

        events: function (info, successCallback, failureCallback) {

            axios
                .post("/sc/list/", {
                    start_date: info.start.valueOf(),
                    end_date: info.end.valueOf(),
                })
                .then((response) => {
                    calendar.removeAllEvents();
                    successCallback(response.data);
                })
                .catch(() => {
                    // バリデーションエラーなど
                    alert("登録に失敗しました");
                });
        },

    });

    calendar.render();

これでプログラムは完成です。

イベントを登録してみましょう。
DBにも格納されますので、ブラウザを閉じて再度カレンダーを表示してもイベントは表示されます。(永続化)

完成ソース

今回作成したプロジェクトのソース全容は、以下のリポジトリをご覧ください。

GitHub - chigusa-web/djangoCalendarApp: Djangoでカレンダー(スケジュール管理)アプリケーション(FullCalendar)
Djangoでカレンダー(スケジュール管理)アプリケーション(FullCalendar). Contribute to chigusa-web/djangoCalendarApp development by creating an acc...

機密情報の管理

SECRET_KEYなどの機密情報を別ファイルで管理する方法は、以下の記事をご覧ください。

その他

外部サーバーへ公開

作成したアプリは公開して使ってもらいましょう!
Djangoアプリケーションを外部公開する方法をまとめました。

脆弱性対策

脆弱性を抱えたアプリケーションの場合、攻撃を受ける可能性があり大変危険です。
作成したアプリケーションは、脆弱性対策も意識しましょう。

GitHubと連携

GitHubと連携する方法を解説しました。
プロジェクトの管理はGitHubを活用しましょう。

GitHub Copilot

GitHub Copilotを導入し、AIにコーディングをサポートしてもらうこともできます。

さいごに

簡単なスケジュールを登録するWebアプリケーションを作成しました。
カレンダーを利用するケースはよくあると思いますが、活用してみましょう。

他にも私のブログで、Djangoについて解説している記事がありますのでご覧ください。

\オススメ/

コメント