How do I use HTMX with Django class based views?
Asked Answered
M

1

5

I'm fairly new to htmx and django. I can implement htmx through function-based views OK, as I just call the function and return a partial.html file into the page.

However I don't understand how to make it work with class-based views I'm now using in Django.

I'd like to load comments on a blog post when a user clicks the Load comments button. My thought is that I'd need to use htmx to do a swap or insertion into a point on my page where I want the comments loaded.

Here is my DetailView where I load the detail of an individual post.

class PostDetailView(DetailView):
    model = Post
    template_name = "post_detail.html"

    def get(self, request, **kwargs):
        print("method is being called")
        return render(request, "partial.html")

Here is my partial.html file that I'd like to be loaded into the page once the button on my post_detail.html page is clicked:

<h2 class="mt-2 mb-2">Comments:</h2>
{% if post.comments.all %}
    {% for comment in post.comments.all %}
        <div class="relative grid grid-cols-1 gap-4 p-4 mb-8 border rounded-lg bg-white shadow-lg">
            <div class="relative flex gap-4">
                <div class="flex flex-col w-full">
                    <div class="flex flex-row justify-between">
                        <p class="relative text-xl whitespace-nowrap truncate overflow-hidden">{{ comment.commenter }}</p>
                        <a class="text-gray-500 text-xl" href="#"><i class="fa-solid fa-trash"></i></a>
                    </div>
                    <p class="text-gray-400 text-sm">{{ comment.date_added }}</p>
                </div>
            </div>
            <p class="-mt-4 text-gray-500">{{ comment.body }}</p>
        </div>
    {% endfor %}
    {% else %}
        <p>No comments</p>
{% endif %}

And here's the button in my post_detail.html page which I'm trying to use to send a get or post (I've tried both) request to my post_detail url/view.

                            <button hx-get="{% url 'posts:post_detail' %}" hx-target="#here">Press</button>
                    </div>
                <div id="here">Swap this text out</div>

Finally, here's my urls.py file:

app_name = "posts"
urlpatterns = [
    path("create/", CreatePostView.as_view(), name="create_post"),
    path("home/", HomeView.as_view(), name="home"),
    path("post/<int:pk>", PostDetailView.as_view(), name="post_detail"),
    path("edit/<int:pk>", EditPostView.as_view(), name="edit_post"),
]

My understanding was that I needed to use htmx to send a get or post request to my class based view PostDetailView which would call the get() or post()method and return my partial.html into the hx-target div with the id of "here".

Where am I going wrong?

Mandelbaum answered 8/7, 2023 at 11:19 Comment(2)
What result are you getting? I think one issue is that the urlpattern for post_detail specifies a primary key parameter, so the button will need to pass it with something like this: <button hx-get="{% url 'posts:post_detail' object.id %}" ...Bucket
With the get request shown above my partial.html template is rendered on the entire page as soon as I go to the PostDetailView. It's only meant to be rendered when I click a button in the on the Post Detail page itself, and obviously I just want the partial.html file to be loaded in to part of the Post Detail page, not all of itMandelbaum
M
7

In your View, you need to render the base template instead on first load i.e. normal get request. But htmx get requests triggered from the button click should render the partial instead.

Requests triggered using htmx have a hx-request header appended to them, so it is possible to identify the source.

class PostDetailView(DetailView):
    model = Post
    template_name = "post_detail.html"

    def get(self, request, **kwargs):
        print("method is being called")
        is_htmx = request.headers.get('HX-Request') == True
        if is_htmx:
            return render(request, "partial.html")

        return render(request, self.template_name)
Merrill answered 10/7, 2023 at 22:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.