@sumaburanyannko

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

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'),

]

自分で試したこと

ここに問題・エラーに対して試したことを記載してください。

0 likes

No Answers yet.

Your answer might help someone💌