Home > Software engineering >  405 HTML: Method Not Allowed
405 HTML: Method Not Allowed

Time:11-09

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 to EventView.
  • 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')
  • Related