I have created a calendar that is embedded in my website. There is an option for admins to create events and when I try to create an event, I get faced with a 405 Error. Could someone please point me in the right direction into why this isn't working, when in previous versions of Python Django, it was working?
My code is below for convenience:
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" >\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 (everything calendar related)
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 (calendar related only)
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 >
<form method="POST">
{% csrf_token %}
<legend >Create New Event</legend>
{{ form | crispy }}
<button type="submit" >
<i ></i>
Submit
</button>
</form>
<hr>
<button >
<i ></i>
<a href="{% url 'main_website_calendar' %}"> Back to Calendar</a>
</button>
</div>
{% endblock %}
CodePudding user response:
Your calendar/event/new/
route points to EventView
, which only has a get method defined. Posting to a view without a post method will produce a 405 error.
Also, your EditEventView doesn't edit an event, but instead always creates a new event.
To fix this, you need to do this:
- Copy the post method in
EditEventView
toEventView
. - Change the original post method in
EditEventView
to only edit existing events:
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')
You should also implement some error handling in these post requests, but I'll leave that to you.
EDIT:
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')