Post view fails with a Method Not Allowed (POST) but setup seems all right?

Hi, first post here. I fiddled with my setup a lil bit too much and all of the sudden the post decorators won’t do their job?

Starting with this rendered form:

<button class="group inline-block ..." type="submit" form="proposal_editor">
    my cool button
</button>

<form class="grow mt-12" id="proposal_editor" method="post" action="/proposal">
    <input type="hidden" name="csrfmiddlewaretoken" value="vTP1GghYFakaIbOTC0CVmw3...WTOOY">
    ... my beautiful form
</form>

Then the urls.py file:

urlpatterns: List[URLPattern] = [
    # ...
    path("proposal", ProposalViews.editor, name="create_form"), # get route
    path("proposal", ProposalViews.submission, name="create"), # post route (100% used to work)
    path("proposal/<str:slug>", ProposalViews.full, name="read"),
    path("proposal/<str:slug>/edit", ProposalViews.editor, name="edit_form"),
    path("proposal/<str:slug>/edit", ProposalViews.submission, name="edit"),
    # ...
]

Those are actually simple func views kept in a class to share common methods and organise stuff a bit:

    @staticmethod
    @require_http_methods(["POST"]) 
    def submission(
        request: HttpRequest, slug: Optional[str] = None
    ) -> HttpResponse | HttpResponseRedirect:
        proposal: Proposal = (
            get_object_or_404(Proposal, slug=slug) if slug else Proposal()
        )
        submission: ProposalForm = ProposalForm(request.POST, instance=proposal)

        if not submission.is_valid():
            return render(
                request,
                "proposal/editor.html",
                ProposalViews.editor_context(submission),
            )

        submission.save()

        return redirect("core:read", slug=proposal.slug)

I know it’s not very standard but it worked just fine… Until it didn’t.

[30/Mar/2025 18:50:16] "GET / HTTP/1.1" 200 13054
[30/Mar/2025 18:50:19] "GET /proposal HTTP/1.1" 200 16702
Method Not Allowed (POST): /proposal
[30/Mar/2025 18:50:44] "POST /proposal HTTP/1.1" 405 0
[30/Mar/2025 19:08:22] "GET /proposal HTTP/1.1" 200 16702

Anyone can spot a maybe silly and obvious mistake I’ve made? I’m quite far along in an exciting project and don’t feel like backtracking for days… :anxious_face_with_sweat:

It’s a simple Django project without DRF.

Welcome @dialka !

This second view (ProposalViews.submission) will never be called because of the duplicate URL.

Review the docs at URL dispatcher | Django documentation | Django

1 Like

I was so sure it worked before! And the first view has a @require_http_methods(["GET"]) … But I guess Django really doesn’t care about HTTP methods. Damn.

Thanks, I’ll figure something out.

It does. Just not at the same level as URLs.

Yeah, coming from other frameworks is a bit of a mindset shift as the method is often part of the route resolution and what makes a path unique (which makes for “prettier”, more semantic URLs I guess).

In Django you’d usually have one view for that URL which contains the code for handling both the display of the form, and the processing of the POST (which might involve displaying the form if there were form errors).