برنامه نویسی

ایجاد یک برنامه To-do با HTMX و Django، قسمت 9: جستجوی فعال

خوش آمدید! در بخش 9، یک ویژگی دیگر را اضافه خواهیم کرد که نشان می‌دهد چگونه HTMX می‌تواند تجربه وب غنی رایج را با کد بسیار کم ارائه دهد: جستجوی فعال.

نتیجه ارسال به این صورت خواهد بود POST درخواست می کند /tasks/search و نتایج را در لیست عوض کنید.

جستجوی فعال به طور خودکار فهرست کارها را با تایپ کاربر فیلتر می کند

اضافه کردن URL و نمایش

ما به یک URL جدید نیاز داریم، /tasks/search، که الف دریافت خواهد کرد POST درخواست با یک پارامتر فراخوانی شده است query.

در core/urls.py مسیر را اضافه می کنیم

# core/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("tasks/", views.tasks, name="tasks"),
    path("tasks//toggle/", views.toggle_todo, name="toggle_todo"),
    path("tasks//", views.task_details, name="task_details"),
    path("tasks//edit/", views.edit_task, name="edit_task"),
    path("tasks/search/", views.search, name="search"), # <-- NEW
]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در core/views.py روش جدید را اضافه می کنیم

# core/views.py

... previous code

@login_required
@require_http_methods(["POST"])
def search(request):
    query = request.POST.get("query")

    if not query:
        return redirect("tasks")

    results = request.user.todos.filter(title__icontains=query).order_by("-created_at")

    return render(
        request,
        "tasks.html#todo-items-partial",
        {"todos": results},
        status=HTTPStatus.OK,
    )
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولین نکته جالب رسیدگی به پرس و جوهای خالی است. هنگامی که کاربر فیلد جستجو را پاک می کند، می توانیم به سادگی به آن تغییر مسیر دهیم tasks view، که تمام موارد انجام کار را برمی گرداند و رفتار اسکرول نامحدود را حفظ می کند.

هنگامی که پرس و جو خالی نیست، موارد انجام کار را با فیلتر می کنیم icontains، که به حروف کوچک و بزرگ حساس نیست و موارد منطبق را برمی گرداند.

یک بار دیگر از الگوی جزئی خود استفاده می کنیم، todo-items-partial، که لیستی از کارهای انجام شده را می پذیرد.

افزودن ورودی جستجو در قالب

در tasks.html قالب یک ورودی جدید در بالای صفحه اضافه می کنیم و آن را به url/view جدید خود وصل می کنیم


..previous code

  
class="w-full max-w-2xl">

The code to fire the search url on change, with debouncing, is straight from the examples section of the official HTMX site.

It's nice how self-explanatory the hx-trigger attribute reads: whenever the input is changed, with a delay (debounce) of 500 milliseconds, or when the ENTER key is pressed.

hx-target informs that we want to place the results in the #todo-items element (the ul), and since we want to change its children, we don't need to specify hx-swap, its default value is what we want already, innerHTML.

While we're checking the code working,let's have a look in developer tools/network, to see the requests being fired.

جستجوی فعال در عمل، نمایش درخواست‌ها و پاسخ‌ها در ابزارهای توسعه‌دهنده

Testing the view

Let's add some tests to our new view, in test_view_tasks.py

The first test is our "happy path", we want to ensure we just return the items searched, which are two of the three fake items we have in the test. The other two test for no matches and empty searches.

@pytest.mark.django_db
def test_search_filtering(client, make_todo, make_user):
    user = make_user()
    client.force_login(user)

    make_todo(title="Todo 1", user=user)
    make_todo(title="Another Todo", user=user)
    make_todo(title="Something else", user=user)

    response = client.post(reverse("search"), {"query": "Todo"})
    content = response.content.decode()

    assert "Todo 1" in content
    assert "Another Todo" in content

    assert "Something else" not in content

@pytest.mark.django_db
def test_search_zero_matches_returns_empty_list(client, make_todo, make_user):
    user = make_user()
    client.force_login(user)

    make_todo(title="Todo 1", user=user)
    make_todo(title="Another Todo", user=user)
    make_todo(title="Something else", user=user)

    response = client.post(reverse("search"), {"query": "Nonexistent"})
    content = response.content.decode()

    assert not any(
        todo in content for todo in ["Todo 1", "Another Todo", "Something else"]
    )

@pytest.mark.django_db
def test_search_empty_query_redirects_to_all_tasks(client, make_todo, make_user):
    user = make_user()
    client.force_login(user)

    make_todo(title="Todo 1", user=user)
    make_todo(title="Another Todo", user=user)
    make_todo(title="Something else", user=user)

    response = client.post(reverse("search"), {"query": ""})
    assert response.status_code == HTTPStatus.FOUND  # redirect
    assert response.url == reverse("tasks")

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

انجام همه آزمایش‌ها برای اطمینان از اینکه همه چیز خوب است

❯ uv run pytest
Test session starts (platform: darwin, Python 3.12.8, pytest 8.3.4, pytest-sugar 1.0.0)
django: version: 5.1.4, settings: todomx.settings (from ini)
configfile: pyproject.toml
plugins: sugar-1.0.0, django-4.9.0
collected 10 items

 core/tests/test_todo_model.py ✓                                                                                                                                    10% █
 core/tests/test_view_tasks.py ✓✓✓✓✓✓✓✓✓                                                                                                                           100% ██████████

Results (0.53s):
      10 passed
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

یادداشتی در مورد آزمایش زمینه و الگوها

ممکن است کسی که تجربه بیشتری در آزمایش الگوهای جنگو داشته باشد تعجب کند که چرا ما آن را آزمایش می کنیم محتوا از الگوی رندر شده، و نه زمینه تصویب می شود، که قاطعانه تر است، به این ترتیب:

   ...
   response = client.post(reverse("search"), {"query": "Todo"})
   context = response.context

   assert {todo.title for todo in context["todos"]} == {"Todo 1", "Another Todo"}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این به دلیل یک مشکل فعلی در django-template-partials، شماره 54 است که هنوز برطرف نشده است (من آن را انجام می دهم، اما بی اهمیت نیست..)

با توجه به این مسئله، پاسخ حاوی مقادیری برای context و templates.

به هر حال، این برای جستجوی فعال است! در مجموع، کد زیادی برای بهبود فوق العاده در UX وجود ندارد. طبق معمول نسخه نهایی این کد را می توانید در شعبه آن https://github.com/rodbv/todo-mx/tree/part-9 پیدا کنید. به سلامتی

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا