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