如何在htmx和Django中创建动态表单?



我希望在Djangohtmx创建一个动态网站,该网站具有搜索栏,用户可以在其中输入关键字并获得搜索结果,而无需重新加载页面。为此,我打算使用 htmx .提交搜索栏不是通过提交按钮完成的,而是通过键盘上的Enter键完成的。提交此搜索栏时,它会通过POST方法将输入的关键字发送到 Django 的views.py文件中,该方法将负责将结果返回到地址localhost:8000/search和搜索栏页面上。这是我views.py文件的内容:

from django.shortcuts import render 
import requests 
from bs4 import BeautifulSoup as bs

from search.models import MyForm

def index(request):
return render(request, 'index.html')
def search(request):
if request.method == 'POST':
search = request.POST['search']
form = MyForm(request.POST)

max_pages_to_scrap = 15
final_result = []
for page_num in range(1, max_pages_to_scrap+1):
url = "https://www.ask.com/web?q=" + search + "&qo=pagination&page=" + str(page_num)
res = requests.get(url)
soup = bs(res.text, 'lxml')
result_listings = soup.find_all('div', {'class': 'PartialSearchResults-item'})
for result in result_listings:
result_title = result.find(class_='PartialSearchResults-item-title').text
result_url = result.find('a').get('href')
result_desc = result.find(class_='PartialSearchResults-item-abstract').text

final_result.append((result_title, result_url, result_desc))
context = {'final_result': final_result, 'form':form}

return render(request, 'index.html', context)
else:
return render(request, 'index.html')

它是刮擦 Ask.com 站点的刮板。对于将发送给用户的提交表单,我首先尝试了以下方法:

<form method="POST" action="search">
...
<input  type="search" name="search"  hx-post="/search" hx-target="#results"  hx-trigger="keyup changed delay:500ms"   value="{{form.search.value}}" placeholder="Search here..."  autofocus x-webkit-speech/>

</form>

当用户提交此表单时,输入的关键字将通过POST方法发送到 Djangoviews.py文件,这负责返回结果。

我试图替换:

<form method="POST" action="search">
...
<input  type="search" name="search"  hx-post="/search" hx-target="#results"  hx-trigger="keyup changed delay:500ms"   value="{{form.search.value}}" placeholder="Search here..."  autofocus x-webkit-speech/>

</form>

由:


<input  type="search" name="search"  hx-post="/search" hx-target="#results" hx-sync="closest form:abort"  hx-trigger="keyup changed delay:500ms"   value="{{form.search.value}}" placeholder="Search here..."  autofocus x-webkit-speech/>

</form>

我也试过这个:

<! DOCTYPE html>
<html> 
<head> 
<meta charset="utf-8"/>
<script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"></script>
. . .
<title>Search page</title>

</head>
<body> 
<div class="search">
<div class="header">

<header >

{% csrf_token %}
<input  type="search" name="search" hx-sync="closest form:abort" hx-post="/search" hx-target="#results"  hx-trigger="keyup changed delay:500ms"   value="{{form.search.value}}" placeholder="Search here..."  autofocus x-webkit-speech/>



</header>
</div>
</div>
<div id="results">
{% if final_result %}
<b style="padding-left: 50px; padding-right: 50px; color: #646464;">Web Results</b>
{% for result in final_result %}
<div style="padding-left: 50px; padding-right: 50px;">
<h3 style="color: blue;  font-size:20px; "><a href="{{ result.1 }}">{{ result.0 }}</a></h3>
<h3 style="color: green; font-size:14px;">{{ result.1 }}</h3>
<h3 style="font-size:17.25px; " >{{ result.2 }}</h3>
</div><br><br>
{% endfor %}
{% endif %}

</div>
</body>
</html>

但没有成功。

根据更多信息更新了答案。

您需要考虑您的页面要求和返回的内容。

HTMX是一种通过ajax请求信息的方式,没有多余的javascript等。 我们将响应放入现有页面中,因此我们想要html,但我们不想要整个页面的HTML - 只是新部分。

你的 django 模板 index.html 由服务器提供。 它不知道 ajax 返回了什么,因为这都是在服务器交付 index.html之后发生的,所以你不能使用它格式化新的 HTML(除非你重新加载整个页面,这违背了使用 HTMX 的重点)

假设您有一个页面,我们将其称为/index 以供参考。 它调用 htmx 代码,以便我们可以使用它。放在索引.html的<head>部分。

<script src="https://unpkg.com/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"></script>

在此之下,以及在<head>seaction(来自各种文档)中,我们将放入以下内容,以确保我们将必要的CSRF令牌链接到htmx ajax帖子

<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>

在该页面上,您还有一个htmx搜索字段。 我们将使用您的

<input  type="search" name="search"  
hx-post="/search" hx-target="#results"  
hx-trigger="keyup changed delay:500ms"   
value="{{form.search.value}}" placeholder="Search here..."  
autofocus x-webkit-speech/>

您的hx触发器显示,在键入或更改时(键入,单击或按回车键后,等待半秒钟后)提交到视图域/搜索并将结果放入<div id="results"></div>

显然,我们需要确保域/搜索在 urls.py 中有一个条目,以便可以找到视图。

现在 - 因为我们希望这一切都在不加载页面的情况下发生,所以我们不能指望 django 的 index.html 模板来处理格式(服务器已经完成了将页面交付到浏览器的工作,我们不想重新加载整个页面和索引.html是整个页面的 HTML)。 所以我们的/search 必须使用另一个模板来渲染 html。我们称之为搜索.html

搜索.html

{% if final_result %}
<b style="padding-left: 50px; padding-right: 50px; color: #646464;">Web Results</b>
{% for result in final_result %}
<div style="padding-left: 50px; padding-right: 50px;">
<h3 style="color: blue;  font-size:20px; "><a href="{{ result.1 }}">{{ result.0 }}</a></h3>
<h3 style="color: green; font-size:14px;">{{ result.1 }}</h3>
<h3 style="font-size:17.25px; " >{{ result.2 }}</h3>
</div><br><br>
{% endfor %}
{% endif %}

我们在 views.py 搜索中称之为索引而不是索引.html

return render(request, 'search.html', context) #context includes final_result

因此,在这种情况下,我们返回的是一个 HTML片段而不是整个页面,这就是应该显示在结果div 中的内容。