Django/HTMX: Removing a many-to-many relationship through a form

(I realize many of you may not have HTMX experience, but hopefully somebody can help me out here)

I’ve been experimenting with HTMX, but now unfortunately am stuck while trying to implement AJAX functionality into my already working Django web app.

The situation is this: I have a many-to-many relationship between my Bet and Tag models. I’m trying to use a button (wrapped inside a POST form) to send a POST request to the URL of a function based view that removes the many-to-many relationship between particular instances of the models. However, for some reason it seems that the function based view is never called upon.

I’m only just starting to use HTMX so I’m pretty confused on what could be wrong. I had a simpler project with HTMX where I got a similar case to work just fine.

The view:

def remove_bet_tag(request):
    print("this is working!!")
    bet_id = request.POST.get("bet-id")
    removed_tag = Tag.objects.get(id=request.POST.get('tag-id'))
    removed_tag.associated_bets.remove(Bet.objects.get(id=bet_id))
    

    return render(request, 'tags/partials/bet_tag_list.html')

(the print statement never is actually rendered to the terminal)

URL patterns:

from django.urls import path
from .views import TagListView, NewTagView, UpdateTagView, DeleteTagView, BetTagPageView, BetNewTagView, remove_bet_tag

urlpatterns = [
    path("", TagListView.as_view(), name="tag_list"),
    path("new_tag/", NewTagView.as_view(), name="new_tag"),
    path("update_tag/<uuid:pk>/", UpdateTagView.as_view(), name="update_tag"),
    path("delete_tag/<uuid:pk>/", DeleteTagView.as_view(), name="delete_tag"),
    path("bet_tag_page/<uuid:pk>/", BetTagPageView.as_view(), name="bet_tag_page"),
    path("bet_new_tag/", BetNewTagView.as_view(), name="bet_new_tag"),

]

htmx_urlpatterns = [

    path("remove_bet_tag/<uuid:pk>/", remove_bet_tag, name="remove_bet_tag"),
    
]

urlpatterns += urlpatterns + htmx_urlpatterns


The template containing the form:

{% for tag in bet.tags.all %}
    <tr>
        <td><b>{{ tag }}</b></td>
        <td><i>{{ tag.description }}</i></td>
        <td>
            <form action="{% url 'bet_tag_page' pk=bet.pk %}" style="display:inline;">
                {% csrf_token %}
                <input type="hidden" name="tag-id" value="{{tag.id}}">
                <input type="hidden" name="bet-id" value="{{bet.pk}}">
                <button hx-post="{% url "remove_bet_tag" bet.pk  %}"
                        hx-target="#bet_tag_list"
                        class="btn btn-danger border border-dark" 
                        type="submit" 
                        name="remove-tag">

                            Remove Tag
                </button>
            </form>
        </td>
    </tr>
{% endfor %}

Do you have hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' somewhere on your page? You can add this to your <body> tag and it will apply to all elements on the page, or you could be more specific and put it closer to your HTMX calls - hard to say without seeing the whole template.

Check out the following page for some tips: Tips - django-htmx 1.21.0 documentation You don’t need to use the django-htmx package, it just adds some nice helpers to your project. I usually just add the bits and pieces myself, since I’m wary about adding dependencies.

Your form and the button each point to different endpoints. Htmx is probably prioritising the form endpoint over the button one. Have a look at Hx-sync if this is intended, otherwise just use one endpoint.

CSRF is not the problem. I have the hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' snippet in my body tag, and no form method-related error is being thrown.

OK, so it sounds like it’s not even getting to your view function. Are you sure that bet.pkis a valid uuid? When you look at the HTML source of the rendered template, does the URL in the hx-post attribute look correct? Are you seeing an attempted POST request in your browser’s dev tools?

Thanks for the replies. I’m going to give it another go now.

I finally got the AJAX to work as intended.

There were 2 problems:
1. The view function was missing an argument after request.
2. The template I posted is an HTML partial that is then injected into another HTML page using {% include %}. The hx-target element I was using was actually in the parent template, and thus not being accessible to the HTMX script within the HTML partial.

Thanks to everybody for your help :green_heart:

1 Like