You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django网站创建日历事件出现405 Method Not Allowed错误求助

问题:创建日历事件时出现405 Method Not Allowed错误

管理员在创建日历事件时遭遇405错误,该功能在旧版Django中可正常运行,相关代码如下:


urls.py

from django.urls import path

from main_website import views
from main_website.views import (AboutView, ArticleCreateView,
                                ArticleDeleteView, ArticleUpdateView,
                                CalendarView, ContactUsView, DonateView,
                                EditEventView, EventView, HelpUsView,
                                ImageAddView, ImageCategoryAddView,
                                ImageDetailView, ImageGalleryView, IndexView,
                                SearchView, TaggedView)

urlpatterns = [
    path('', IndexView.as_view(), name='main_website_home'),
    path('about', AboutView.as_view(), name='main_website_about'),
    path('search/', SearchView.as_view(), name='main_website_search'),
    path('article_create', ArticleCreateView.as_view(), name='main_website_article_create'),
    path('article/<slug:slug>', views.article_detail, name='main_website_article_detail'),
    path('article/update/<slug:slug>/', ArticleUpdateView.as_view(), name='main_website_article_update'),
    path('article/delete/<slug:slug>/', ArticleDeleteView.as_view(), name='main_website_article_delete'),
    path('tags/<slug:slug>/articles/', TaggedView.as_view(), name="main_website_article_tags"),
    path('calendar/', CalendarView.as_view(), name='main_website_calendar'),
    path('calendar/event/new/', EventView.as_view(), name='main_website_calendar_new_event'),
    path('calendar/event/edit/<event_id>/', EditEventView.as_view(), name='main_website_calendar_edit_event'),
    path('waiting_list/register', views.waiting_list_register, name='main_website_waiting_list_register'),
    path('gallery', ImageGalleryView.as_view(), name='main_website_gallery'),
    path('gallery/upload', ImageAddView.as_view(), name='main_website_gallery_upload'),
    path('gallery/category/add/', ImageCategoryAddView.as_view(), name='main_website_gallery_add_category'),
    path('gallery/image/<str:pk>/', ImageDetailView.as_view(), name='main_website_gallery_image_detail'),
    path('help_us', HelpUsView.as_view(), name='main_website_help_us'),
    path('ways_to_donate', DonateView.as_view(), name='main_website_ways_to_donate'),
    path('contact_us', ContactUsView.as_view(), name='main_website_contact_us')
]

utils.py

import calendar
from calendar import HTMLCalendar
from datetime import date, datetime, timedelta

from main_website.models import Event


class Calendar(HTMLCalendar):
    def __init__(self, year=None, month=None):
        self.year = year
        self.month = month
        super(Calendar, self).__init__()

    # 格式化日期单元格
    # 按日期筛选事件
    def formatday(self, day, events):
        events_per_day = events.filter(start_time__day=day)
        d = ''
        for event in events_per_day:
            d += f'<li> {event.get_html_url} </li>'

        if day != 0:
            return f"<td><span class='date'>{day}</span><ul> {d} </ul></td>"
        return '<td></td>'

    # 格式化周行
    def formatweek(self, theweek, events):
        week = ''
        for d, weekday in theweek:
            week += self.formatday(d, events)
        return f'<tr> {week} </tr>'

    # 格式化月日历表格
    # 按年月筛选事件
    def formatmonth(self, withyear=True):
        events = Event.objects.filter(start_time__year=self.year, start_time__month=self.month)

        cal = f'<table border="0" cellpadding="0" cellspacing="0" class="calendar">\n'
        cal += f'{self.formatmonthname(self.year, self.month, withyear=withyear)}\n'
        cal += f'{self.formatweekheader()}\n'
        for week in self.monthdays2calendar(self.year, self.month):
            cal += f'{self.formatweek(week, events)}\n'
        return cal

def get_date(req_month):
    if req_month:
        year, month = (int(x) for x in req_month.split('-'))
        return date(year, month, day=1)
    return datetime.today()

def prev_month(d):
    first = d.replace(day=1)
    prev_month = first - timedelta(days=1)
    month = 'month=' + str(prev_month.year) + '-' + str(prev_month.month)
    return month

def next_month(d):
    days_in_month = calendar.monthrange(d.year, d.month)[1]
    last = d.replace(day=days_in_month)
    next_month = last + timedelta(days=1)
    month = 'month=' + str(next_month.year) + '-' + str(next_month.month)
    return month

def get_filename(filename):
    return filename.upper()

views.py(日历相关代码)

import calendar
from datetime import date, datetime, timedelta

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.safestring import mark_safe
from django.views import generic
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
from taggit.models import Tag

from main_website.forms import (AddImageCategoryForm, ArticleForm, EventForm,
                                UploadImageForm, WaitingListForm)
from main_website.models import (Article, Event, ImageGallery,
                                 ImageGalleryCategory)
from main_website.utils import Calendar, get_date, next_month, prev_month

class CalendarView(generic.ListView):
    model = Event
    template_name = 'calendar/calendar.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        d = get_date(self.request.GET.get('month', None))
        cal = Calendar(d.year, d.month)
        html_cal = cal.formatmonth(withyear=True)
        context['calendar'] = mark_safe(html_cal)
        context['prev_month'] = prev_month(d)
        context['next_month'] = next_month(d)
        context['title'] = 'Calendar'
        return context

class EventView(LoginRequiredMixin, generic.View):
    def get(self, request, event_id=None):
        instance = Event()
        if event_id:
            instance = get_object_or_404(Event, pk=event_id)
        else:
            instance = Event()

        form = EventForm()

        context = { 
            'form': form,
            'instance': instance,
            'title': 'New Event',
        }
        return render(request, 'calendar/event.html', context)

class EditEventView(LoginRequiredMixin, generic.View):
    def get(self, request, event_id=None):
        instance = Event()
        if event_id:
            instance = get_object_or_404(Event, pk=event_id)
        else:
            instance = Event()

        form = EventForm()

        context = { 
            'form': form,
            'instance': instance,
            'title': 'Edit Event',
        }
        return render(request, 'calendar/edit_event.html', context)

    def post(self, request):
        instance = Event()
        form = EventForm(request.POST or None, instance=instance)
        if form.is_valid():
            form.save()
            return redirect('main_website_calendar')

forms.py(日历相关代码)

from django import forms
from django.forms import DateInput
from taggit.forms import TagField
from taggit_labels.widgets import LabelWidget

from accounts.models import User
from main_website.models import (Article, Event, ImageGallery,
                                 ImageGalleryCategory, WaitingList)

class EventForm(forms.ModelForm):
    class Meta:
        model = Event
        # 使用HTML5 datetime-local输入控件,指定格式让日期正常显示
        widgets = {
            'start_time': DateInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'),
            'end_time': DateInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'),
        }
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(EventForm, self).__init__(*args, **kwargs)
        # 设置输入格式,解析HTML5 datetime-local输入为Django datetime字段
        self.fields['start_time'].input_formats = ('%Y-%m-%dT%H:%M',)
        self.fields['end_time'].input_formats = ('%Y-%m-%dT%H:%M',)

event.html

{% extends 'calendar/base.html' %}
{% load crispy_forms_tags %}

{% block content %}
<div class="calendar-section">
    <form method="POST">
        {% csrf_token %}
        <legend class="border-bottom mb-4">Create New Event</legend>
        {{ form | crispy }}
        <button type="submit" class="btn btn-custom-purple">
            <i class="fa-duotone fa-calendar-plus"></i> 
            Submit
        </button>
    </form>
    <hr>
    <button class="btn btn-custom-purple">
        <i class="fa-duotone fa-angles-left"></i>
        <a href="{% url 'main_website_calendar' %}"> Back to Calendar</a>
    </button>
</div>
{% endblock %}

问题原因及修复方案

核心问题

EventView类仅实现了get方法,未处理POST请求。当表单以POST方式提交时,视图没有对应处理逻辑,直接返回405错误。此外EditEventView的post方法也存在逻辑错误:总是创建新实例,未使用传入的event_id获取待编辑事件。

修复步骤

  1. 给EventView添加post方法,同时修正get方法中表单未绑定实例的问题:
class EventView(LoginRequiredMixin, generic.View):
    def get(self, request, event_id=None):
        instance = get_object_or_404(Event, pk=event_id) if event_id else Event()
        form = EventForm(instance=instance)  # 绑定实例,编辑时自动填充数据

        context = { 
            'form': form,
            'instance': instance,
            'title': 'New Event',
        }
        return render(request, 'calendar/event.html', context)
    
    def post(self, request):
        form = EventForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('main_website_calendar')
        # 表单验证失败时返回原页面并展示错误
        return render(request, 'calendar/event.html', {'form': form, 'title': 'New Event'})
  1. 修正EditEventView的post方法,绑定待编辑的事件实例:
class EditEventView(LoginRequiredMixin, generic.View):
    def get(self, request, event_id=None):
        instance = get_object_or_404(Event, pk=event_id) if event_id else Event()
        form = EventForm(instance=instance)

        context = { 
            'form': form,
            'instance': instance,
            'title': 'Edit Event',
        }
        return render(request, 'calendar/edit_event.html', context)

    def post(self, request, event_id):
        instance = get_object_or_404(Event, pk=event_id)
        form = EventForm(request.POST, instance=instance)
        if form.is_valid():
            form.save()
            return redirect('main_website_calendar')
        return render(request, 'calendar/edit_event.html', {'form': form, 'instance': instance, 'title': 'Edit Event'})
  1. 推荐优化:使用Django内置的CreateView和UpdateView简化代码,减少重复逻辑:
# 替换原EventView
class EventView(LoginRequiredMixin, CreateView):
    model = Event
    form_class = EventForm
    template_name = 'calendar/event.html'
    success_url = reverse_lazy('main_website_calendar')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'New Event'
        return context

# 替换原EditEventView
class EditEventView(LoginRequiredMixin, UpdateView):
    model = Event
    form_class = EventForm
    template_name = 'calendar/edit_event.html'
    success_url = reverse_lazy('main_website_calendar')
    pk_url_kwarg = 'event_id'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = 'Edit Event'
        return context

内容的提问来源于stack exchange,提问作者Kiran Sarda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:00:56