Django and HTMX active search issue

Question:

I’m trying to make an active search with HTMX in Django, and it’s working, but happens that if I delete the search terms in the form the entire page is rendered twice and can`t fix it.

This is ok:

Everything is ok

But this happens if I delete the text introduced in the form:

...but if I erase the form..

view.py

class SearchView(View):
@staticmethod
def get(request):
    search_term = request.GET.get('search', None)
    if search_term:
        roads = Road.objects.filter(name__contains=search_term).all()[:100]
        template = 'search_results.html'
    else:
        roads = []
        template = 'pkes/search.html'
    return render(request=request,
                  template_name=template,
                  context={
                      'roads': roads,
                  })

search.html

{% extends "pkes/base.html" %}
{% block content %}
<form action="/action_page.php">
    <label for="search">Carretera</label>
    <input class="input"
           name="search"
           type="search"
           placeholder="Ej: EX-118"
           hx-get="/search"
           hx-trigger="keyup changed delay:500ms, search"
           hx-target="#search-results"
           hx-swap="innerHTML"/>
    <label for="kilometro">Kilómetro</label>
    <input class="input" name="kilometro">
    <input type="submit" value="Enviar">
</form>
<div id="search-results">
    {% include "search_results.html" %}
</div>
{% endblock %}

search_results.html

 {% for road in roads %}
<div>
    {{ road.name }}
</div>
{% endfor %} 

Thanks!!!

Django 4.1.6, Python 3.11.1, HTMX 1.8.5

Reference: rockandnull.com

Asked By: EnriqueRBT

||

Answers:

You have two types of requests: the regular one, where the client expects a full rendered page, and the HTMX one, where the client expects just an HTML fragment. You have to identify the type of a request in a view function and return full or partial rendered template to the client. Currently you try to guess the type of the request based on the length of the search term. So when the search term is cleared you return a full page instead of just a fragment causing the double rendering issue.

In this modified view function we have a new variable is_htmx that identify the request type. We set the template (full or fragment) based on this. Furthermore we execute a search query based on the search term or just return an empty list.

class SearchView(View):
@staticmethod
def get(request):
    search_term = request.GET.get('search', None)
    is_htmx = self.request.headers.get('HX-Request') == 'true'

    template = 'search_results.html' if is_htmx else 'pkes/search.html'
    roads = Road.objects.filter(name__contains=search_term).all()[:100] if search_term else []

    return render(request=request,
                  template_name=template,
                  context={
                      'roads': roads,
                  })

You can also use the Django-HTMX package, so you have a request.htmx attribute set by a middleware that identifies the type of the request.

Answered By: Dauros
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.