405 HTML: Метод не разрешен

Я создал календарь, который встроен в мой сайт. Есть возможность для администраторов создавать события, но когда я пытаюсь создать событие, я сталкиваюсь с 405 ошибкой. Не мог бы кто-нибудь указать мне правильное направление, почему это не работает, когда в предыдущих версиях Python 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__()

    # formats a day as a td
    # filter events by day
    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>'

    # formats a week as a tr
    def formatweek(self, theweek, events):
        week = ''
        for d, weekday in theweek:
            week += self.formatday(d, events)
        return f'<tr> {week} </tr>'

    # formats a month as a table
    # filter events by year and month
    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
        # datetime-local is a HTML5 input type, format to make date time show on fields
        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)
        # input_formats parses HTML5 datetime-local input to datetime field
        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 %}

Ваш маршрут calendar/event/new/ указывает на EventView, у которого определен только метод get. Постинг в представление без метода post приведет к ошибке 405.

Кроме того, ваш EditEventView не редактирует событие, а вместо этого всегда создает новое событие.

Чтобы исправить это, вам нужно сделать следующее:

  • Скопируйте метод post в EditEventView в EventView.
  • Измените исходный метод сообщения в EditEventView, чтобы редактировать только существующие события:

EventEditView post method:

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

Вам также следует реализовать некоторую обработку ошибок в этих почтовых запросах, но я оставляю это на ваше усмотрение.

РЕДАКТИРОВАНИЕ:

EventView post method:

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')
Вернуться на верх