htmx active search not accepting post method

I’m getting the following error and I could really use some help. The htmx docs are good for the html side but I haven’t found examples of the python code so I had to guess a bit but it seems straight-forward. I’ve provided a post function at least.

Method Not Allowed: /players/search-player/
[09/Dec/2022 16:43:31] "POST /players/search-player/ HTTP/1.1" 405 41

views.py

def search_player(request):
  print(request)
  def post(self, request, **kwargs):
    search_text = request.POST.get("search")
    results = Player.objects.filter(name__icontains=search_text)
    context = {'results': results}
    return render(request, 'Striker/players', context)

urls.py

  path('striker/players/search-player/', views.search_player, name='search-player'),
  path('striker/players/', views.PlayerListView.as_view(), name='player.list'),

player_list.html

{% extends './base.html' %}
{% load humanize %}

{% block content %}
<section class="bg-black body-font">
  <div class="container px-5 py-2 mx-auto flex flex-wrap">
    <div class="-m-4">
      <div class="p-4 md:w-full">
        <h1 class="text-2xl mb-2 mt-2 text-emerald-500">Current Members</h1>
        <input class="form-control" type="search"
          name="search"
          hx-post="/players/search-player/"
          hx-trigger="keyup changed delay:500ms, search"
          hx-target="#search-results"
        />
          <div class="flex flex-wrap p-2 sm:flex-row" id="search-results">
            {% for player in players %}
            <div class="box-border h-85 w-60 mb-1">
              <div class="mx-2 rounded-lg border-double border-2 border-emerald-500 px-2 py-2 bordermildblue">
                <div class="bg-gray-800">
                  <h2 class="text-blue-400 text-lg title-font font-medium mb-4 button-85"><a
                      href="{% url 'player.detail' player.pk %}" title="Link to Player's Toons">{{ player.name }}</a>
                  </h2>
                </div>
                <div class="text-left">
                  <p class="leading-relaxed text-base">GP: {{ player.gp|intword }}</p>
                  <p class="leading-relaxed text-base">Toons: {{ player.gpChar|intword }}</p>
                  <p class="leading-relaxed text-base">Ships: {{ player.gpShip|intword|intcomma }}</p>
                  <p class="leading-relaxed text-base">Allycode: <a class="text-blue-500"
                      href="https://swgoh.gg/p/{{ player.allycode }}" Title="Link to SWGOH.gg">{{ player.allycode }}</a>
                    <span class="text-xs">.gg</span>
                  </p>
                </div>
              </div>
            </div>
            {% endfor %}
          </div>
      </div>
    </div>
  </div>
</section>

{% endblock content %}

You’re trying to define a post function in a view function. That’s not how FBV (function-based views) work. In an FBV, you need to code your logic as a function.

As an example of an FBV, see the sample view in the docs

If I change it to:

def search_player(request):
  print(request)
  if request.method == 'POST':
    search_text = request.POST.get("search")
    results = Player.objects.filter(name__icontains=search_text)
    context = {'results': results}
    return render(request, 'Striker/players', context)

per the doc you referenced I get the same error.

How are you running this application? Is this in your development environment using Django’s runserver or in a production environment?

Do you have any other views in your system that are successfully handling a POST?

Oh, you’re posting to the wrong url. Notice the difference between:

and

Well of course. Thanks as always Ken. Now I’m not getting back what I want yet but at least I can move forward.

I must grouch a little though. Why does htmx docs only show one side of the equation? I’m getting bizarre results because when I type into the search box the list (id=‘search-results’) disappears and all I get is another copy of the search box. I’ll figure it out but some examples of the views part of the equation would sure be nice.

Because htmx is a “front-end” tool that will work with any reasonable back end regardless of the framework or language.

What htmx will do is issue http requests, it doesn’t matter what receives them.

What the server needs to respond with for htmx is an html fragment containing the divs to replace the corresponding divs on the existing page.

Just to close this out, I needed to create a partial to display the search results and then render the partial. After thinking it through (and re-visiting a youtube series I had watched) it became clear.

Also I do realize that htmx is backend-agnostic, however given that they have a lot of django-related questions on their forum it would just seem to make sense to provide better documentation for that combo. I’m not saying I expect them to, just that it would be nice. (As I said, just being grouchy.)

All’s well that ends well.

Hi, I ran into the same problem! The whole thing works when I use “hx-get”, but not if I use “hx-post”. How did you fix this? I am getting the follwoing error, when I change to POST : Method Not Allowed (POST): /ecrf/documentation/

Thank you!

documentation.html:

          <input class="form-control" type="search" 
                 name="search" placeholder="Begin Typing To Search Study Participants..." 
                 hx-post="{% url 'ecrf:documentation' %}" 
                 hx-trigger="keyup changed delay:500ms, search" 
                 hx-target="#search-results" 
                 hx-indicator=".htmx-indicator"
                 hx-swap="innerHTML">
          
          <div id="search-results">
                {% include "ecrf/documentation/participant_list.html" %}
          </div>

participant_list.html:

{% for participant in participant_list %}
    <div>
        {{ participant.pid }}
    </div>
{% endfor %}

view.py:

class DocumentationView(LoginRequiredMixin, ListView):
    login_url = "ecrf/login/"
    redirect_field_name = "redirect_to"
    
    def get(self, request):
        search_term = request.POST.get('search', None)
        is_htmx = self.request.headers.get('HX-Request') == 'true'

        if is_htmx:
            participant_list = Participant.objects.filter(pid__contains=search_term).all()
            template = 'ecrf/documentation/participant_list.html'
        else:
            participant_list = None
            template = 'ecrf/documentation/documentation.html'
        return render(request=request, 
                      template_name=template,
                      context={
                          'participant_list': participant_list,
                      })

The ListView defines a get method but does not define a default post method. If you’re going to post to a view, then the view needs to handle a post request.

1 Like

Wow! Thank you for this instantaneous reply! I changed the view.py as follows and now it works. Thank you!

@login_required
def documentation_view(request):

    search_term = request.POST.get('search', None)

    if request.method == "POST":
        template = 'ecrf/documentation/participant_list.html'
        if search_term:
            participant_list = Participant.objects.filter(pid__contains=search_term).all()
        else:
            participant_list = None
    else:
        participant_list = None
        template = 'ecrf/documentation/documentation.html'
    return render(request=request, 
                  template_name=template,
                  context={
                      'participant_list': participant_list,
                  })