FullCalendar スロットを右クリックで削除したい
解決したいこと
FullCalendarとDjangoを使ってWebアプリを作ろうとしています。そこでカレンダーに登録したスロットを右クリックで削除できるようにしたいのですが、どうもうまくいかなくて困っています。Chat-GPTでもうまくいきませんでした。あと初心者なので、質問とコードがへたくそかもしれませんがお願いします。
解決方法を教えて下さい。
発生している問題・エラー
ちょっと残ってないので記憶の限りですが確か、Idに空のオブジェクトが渡されています、というメッセージだったはずです
該当するソースコード
該当箇所は[slot2.js]の{eventDidMount}と[views.py]の{DeleteSlotView}と[urls.py]の{urlpatterns}の3か所だと思うのですが、一応全部載せておきます。ほかにもコードファイルはあるので、もし情報が足りないのであれば追加します。
slot2.js(JavaScript)ファイル
// CSRF対策
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
axios.defaults.xsrfCookieName = "csrftoken"
document.addEventListener('DOMContentLoaded', function () {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'timeGridWeek',
// initialView: 'dayGridMonth',
selectable: true,
select: function(info) {
const isConfirmed = confirm("この時間帯にスロットを追加しますか?");
if (isConfirmed) {
axios
.post("/add-slot/", {
start_time: info.start.valueOf(),
end_time: info.end.valueOf(),
})
.then(() => {
// イベントの追加
calendar.addEvent({
title: "slot",
start: info.start,
end: info.end,
allDay: false,
});
})
.catch(() => {
// バリデーションエラーなど
alert("登録に失敗しました");
});
}
},
eventDidMount: function(info) {
info.el.addEventListener('contextmenu', function(ev) {
ev.preventDefault(); // 右クリックメニューを抑制
const isConfirmed = confirm("このスロットを削除しますか?");
if (isConfirmed) {
axios
.post("/delete-slot/", {
id: info.event.id
})
.then(() => {
// イベントの削除
info.event.remove();
})
.catch(() => {
// バリデーションエラーなど
alert("削除に失敗しました");
});
}
});
},
events: function (info, successCallback, failureCallback) {
axios
.post("/get-slot/", {
start_time: info.start.valueOf(),
end_time: info.end.valueOf(),
})
.then((response) => {
calendar.removeAllEvents();
successCallback(response.data);
})
.catch(() => {
// バリデーションエラーなど
alert("読み込みに失敗しました");
});
},
// 15分刻みのスロットを表示
slotDuration: '00:15:00',
// 1時間ごとに時間ラベルを表示
slotLabelInterval: '01:00:00',
});
calendar.render();
});
views.py(python)ファイル
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, JsonResponse, Http404
from django.utils import timezone
from django.template import loader
from django.middleware.csrf import get_token
from django.contrib.auth.decorators import login_required
import datetime
import time
import json
Create your views here.
from .models import AvailableSlot, Subject, CustomUser, Schedule
from .forms import SlotForm
@login_required
def HomeView(request):
user = request.user
schedules = Schedule.objects.filter(teacher=user) | Schedule.objects.filter(learner=user)
return render(request, 'myapp/home.html', {'schedules': schedules})
def ProfileView(request):
user = request.user
return render(request, "myapp/profile.html", {'user': user})
def SlotsView(request):
get_token(request)
template = loader.get_template("myapp/slots.html")
return HttpResponse(template.render())
def AddSlotView(request):
if request.method == "GET":
# GETは対応しない
raise Http404()
# JSONの解析
datas = json.loads(request.body)
# UNIXタイムスタンプを文字列形式の日時に変換
formatted_start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(datas["start_time"] / 1000))
formatted_end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(datas["end_time"] / 1000))
# 登録処理
available_slot = AvailableSlot(
teacher=request.user, # ログイン中のユーザーを取得
start_time=formatted_start_time,
end_time=formatted_end_time,
)
available_slot.save()
# 空を返却
return HttpResponse("")
def DeleteSlotView(request):
if request.method == "POST":
datas = json.loads(request.body)
slot_id = datas.get("id")
# スロットを取得し、削除する
try:
slot = AvailableSlot.objects.get(id=slot_id, teacher=request.user)
slot.delete()
return HttpResponse("")
except AvailableSlot.DoesNotExist:
return HttpResponse("スロットが見つかりません", status=404)
def GetSlotView(request):
if request.method == "GET":
# GETは対応しない
raise Http404()
# JSONの解析
datas = json.loads(request.body)
# UNIXタイムスタンプを文字列形式の日時に変換
formatted_start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(datas["start_time"] / 1000))
formatted_end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(datas["end_time"] / 1000))
# FullCalendarの表示範囲のみ表示
slots = AvailableSlot.objects.filter(
start_time__lt=formatted_end_time, end_time__gt=formatted_start_time
)
# fullcalendarのため配列で返却
slot_list = []
for slot in slots:
slot_list.append(
{
"title": "slot",
"start": slot.start_time.strftime("%Y-%m-%d %H:%M:%S"),
"end": slot.end_time.strftime("%Y-%m-%d %H:%M:%S"),
}
)
return JsonResponse(slot_list, safe=False)
def ReserveView(request):
template = loader.get_template("myapp/reserve.html")
return HttpResponse(template.render())
urls.py(python)ファイル
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
path("", views.HomeView, name="home"),
path("profile/", views.ProfileView, name="profile"),
path("slots/", views.SlotsView, name="slots"),
path("add-slot/", views.AddSlotView, name="add_slot"),
path("get-slot/", views.GetSlotView, name="get_slot"),
path("delete-slot/", views.DeleteSlotView, name="delete_slot"),
path("reserve/", views.ReserveView, name="reserve"),
path('login/', auth_views.LoginView.as_view(template_name='myapp/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(next_page='/login/'), name='logout'),
]
自分で試したこと
ここに問題・エラーに対して試したことを記載してください。