python-3.xdjango-viewsdjango-3.1

Django UpdateView Method Not Allowed (POST)


When I click "save" on the Update page (http://127.0.0.1:8000/account/15/update) I get

[19/Mar/2021 13:47:18] "GET /account/15/update HTTP/1.1" 200 2311
Method Not Allowed (POST): /account/15/
Method Not Allowed: /account/15/
[19/Mar/2021 13:47:24] "POST /account/15/ HTTP/1.1" 405 0

The database record does not get updated, so it looks like something fails silently. And looking at the error message it looks like we end up attempting a POST on the Account Detail page instead of the Account Update page - how did that happen?

Here's my Accounts\urls.py

from django.urls import path
from .views import (
AccountCreateView,
AccountListView,
AccountDetailView,
AccountUpdateView,
AccountDeleteView
)

app_name = 'account'
urlpatterns = [
path('', AccountListView.as_view(), name='account-list'),
path('create/', AccountCreateView.as_view(), name='account-create'),
path('<int:pk>/', AccountDetailView.as_view(), name='account-detail'),
path('<int:pk>/update', AccountUpdateView.as_view(), name='account-update'),
path('<int:pk>/delete', AccountDeleteView.as_view(), name='account-delete'),
]

Here's my Accounts\views.py

from django.shortcuts import render, get_object_or_404, redirect

from django.views.generic import (
    CreateView,
    DetailView,
    ListView,
    UpdateView,
    DeleteView
)

from .forms import AccountForm
from .models import Account

class AccountCreateView(CreateView):
    form_class = AccountForm
    template_name = 'Accounts/account_create.html'
    queryset = Account.objects.all().order_by("-pk")

    def form_valid (self, form):
        return super().form_valid(form)

class AccountDetailView(DetailView):

    def get_object(self):
        account_id = self.kwargs.get("pk")
        return get_object_or_404(Account, account_id=account_id)

class AccountUpdateView(UpdateView):
    form_class = AccountForm
    template_name = 'Accounts/account_create.html'

    def get_object(self):
        account_id = self.kwargs.get("pk")
        return get_object_or_404(Account, account_id=account_id)

    def form_valid (self, form):
        return super().form_valid(form)

And here's my Accounts\account_create.html (which I use both for create and update)

{% extends 'base.html' %}
{% block content %}
    <h1>Account Create/Update Page for {{ request.user }} </h1>
    This is the InApp Account Create/Update Page<p/>
    <form action="." method='POST' enctype="multipart/form-data"> {% csrf_token %}
        <table id="accountdetail">
            {{ form.as_table }}
        </table>
        <p/>
        <input type="submit" value="Save"/> &nbsp;&nbsp;
        <button onclick="location.href='/account'" type="button">Cancel</button>
    </form>
{% endblock %}

What am I missing? Thanks.


Solution

  • Your form in the HTML is explicitly sending POST data to ".":

    <form action="." method='POST' enctype="multipart/form-data">
    

    so I believe /account/15/update becomes /account/15, like moving one directory up. If you really want to use the same template, you could try something like:

    {% if "update" in request.get_full_path %}
     <form action="{% url 'account-update' account.id %}" method="PUT">
    {% else %}
     <form action="{% url 'account-create' account.id %}" method="POST" enctype="multipart/form-data">
    

    Also, note that the enctype attribute can only be used with a method="POST"