Django + HTMX CRUD 应用程序
简介 使用 javascript 编写 Ajax 请求的日子已经一去不复返了,只需向 HTML 内容标签添加一些参数,您就可以准备向后端发送请求了。所以,我们要回到过去,纠正我们对 API 和客户端/服务器端渲染的看法。我们指的是超媒体模型用于利用服务器端的数据处理。让我们用HTMX了解这种古老但革命性的开发方法。 是的,HTMX 可以直接在 HTML 中用于 API/服务器端调用。我们将通过创建
简介
使用 javascript 编写 Ajax 请求的日子已经一去不复返了,只需向 HTML 内容标签添加一些参数,您就可以准备向后端发送请求了。所以,我们要回到过去,纠正我们对 API 和客户端/服务器端渲染的看法。我们指的是超媒体模型用于利用服务器端的数据处理。让我们用HTMX了解这种古老但革命性的开发方法。
是的,HTMX 可以直接在 HTML 中用于 API/服务器端调用。我们将通过创建一个基本的 CRUD 应用程序来探索 HTMX 的基础。
什么是HTMX?
可能出现的第一个问题是什么以及为什么 HTMX? Htmx 是一个很棒的库,它是一个 javascript 库,但是等等。它是一个 javascript 库,旨在允许我们编写更少或根本不编写 javascript。它可以作为一种发送 AJAX 请求的方式,而无需您编写任何 javascript。它直接从 HTML 使用本机浏览器功能。
因此,我们可以使用 HTMX 在 Django 应用程序中创建交互式模板。我们可以使用简单的 HTML 属性(如hx-get
、hx-post
等)动态调用和从服务器获取数据。我们将在本文中介绍这些属性。
您可以在这个GitHub 存储库上查看本文使用的源代码。
设置 Django 项目
我们将从头开始创建一个 Django 项目并设计一个基本的博客类型的应用程序。我们将创建一个非常简单的项目,其中包含几个应用程序,例如用于身份验证的user
和用于我们博客应用程序的 CRUD 部分的article
。
要设置 django 项目,我们可以运行以下命令来快速启动并运行基础 django 项目。
mkdir htmx_blog
python3 -m venv .venv
source .venv/bin/activate
pip install django
django-admin startproject htmx_blog .
进入全屏模式 退出全屏模式
我有一个基本用户模型,用于一些基本 django 项目中的简单身份验证系统,您可以定义自己的用户应用程序或从此处获取应用程序。
因此,话虽如此,我们将使用用户模型作为我们将在接下来定义的文章模型。通过创建基本的注册功能,您就可以开始了!
创建文章应用程序
我们至少需要一个应用程序来使用 htmx,因为稍后我们将在配置 htmx 时定义模型、视图和 URL。
django-admin startapp article
进入全屏模式 退出全屏模式
创建应用程序后,您可以将这些应用程序标签添加到settings.py
文件中的INSTALLED_APPS
配置中。需要将user
应用程序和article
应用程序添加到已安装的应用程序中,以便 django 为与项目相关的各种上下文选择这些应用程序。
# htmx_blog/settings.py
INSTALLED_APPS = [
...
...
...
'article',
'user',
]
进入全屏模式 退出全屏模式
我们已经完成了基本设置,我们还需要更多配置才能使项目正常工作。
设置模板和静态文件
模板将在 htmx 部分中发挥重要作用,因此在涉足 htmx 和客户端数据呈现之前正确配置它们同样重要。
我喜欢将所有模板保存在BASE_DIR
中的单个文件夹中,并为特定应用程序提供单独的子文件夹。还有一个static
文件夹,其中css
、js
和images
作为较大项目的子文件夹。
mkdir templates static
进入全屏模式 退出全屏模式
此外,在设置中配置创建的静态和模板。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
STATIC_URL = 'static/'
STATICFILES_DIRS = [str(BASE_DIR/ "static")]
STATIC_ROOT = BASE_DIR / "staticfiles"
进入全屏模式 退出全屏模式
初始迁移
对 django 项目中的用户模型和默认模型运行迁移命令。
python manage.py makemigrations
python manage.py migrate
进入全屏模式 退出全屏模式
因此,该项目还将包括身份验证简单注册和登录/注销路由。我们将通过创建一个抽象用户来使用默认的 Django 用户模型,以防万一我们需要任何其他属性。
设置 HTMX
我们不需要为使用 HTMX 进行太多配置,因为它是一个 javascript 库,我们可以通过 CDN 调用它或手动安装它并链接静态 javascript 文件。无论哪种方式,两者都一样好,你可能喜欢一个,我可能会喜欢另一个。
如果您已经有一个基本模板,您可以简单地将下面的脚本放在模板的 head 标签内。这将使我们的 htmx 属性可用。
<script src="https://unpkg.com/htmx.org@1.8.0"></script>
进入全屏模式 退出全屏模式
如果您没有基本模板,您可以通过在templates
目录中创建一个 HTML 文件来创建一个。这个名字可以是任何东西,但要小心跟进,因为它对我来说可能会有所不同。我将选择base.html
作为这个项目的模板。它看起来如下所示:
<!-- tempaltes/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTMX Blog</title>
{% load static %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://unpkg.com/htmx.org@1.8.0"></script>
</head>
<body >
<nav>
<h2>HTMX Blog</h2>
<div class="navbar">
{% if user.is_authenticated %}
<a class="nav-item nav-link" href="{% url 'logout' %}"><button class="btn btn-link">Logout</button></a>
{% else %}
<a class="nav-item nav-link" href="{% url 'login' %}"><button class="btn btn-link">Login</button></a>
<a class="nav-item nav-link" href="{% url 'register' %}"><button class="btn btn-link">Register</button></a>
{% endif %}
</div>
</nav>
{% block body %}
{% endblock %}
</body>
</html>
进入全屏模式 退出全屏模式
我有一个带有用户身份验证视图的导航栏,如果用户未登录,则只需一个登录或注册按钮,如果用户已通过身份验证,则有一个注销按钮。我们在 head 标签结束之前添加了来自 CDN 的 htmx 脚本文件。我们还为我们将在这篇文章中创建的体面的 UI 包含了引导 CSS 文件。
这是其中一种方法,可以将 htmx 注入 HTML 模板,您甚至可以从htmx cdn下载 javascript 文件。此外,可以将其下载或粘贴到您的本地文件夹中,并作为静态文件或直接嵌入到 HTML 模板中。
定义模型
我们将从定义我们正在创建的应用程序的模型开始本教程。在这里,我们将创建一个简单的 Article 模型,其中包含一些参数,例如title
、content
、author
等。
from django.db import models
from user.models import Profile
class Article(models.Model):
Article_Status = (
("DRAFT", "Draft"),
("PUBLISHED", "Published"),
)
title = models.CharField(max_length=128, unique=True)
content = models.TextField()
author = models.ForeignKey(Profile, on_delete=models.CASCADE)
status = models.CharField(
max_length=16,
choices=Article_Status,
default=Article_Status[0],
)
def __str__(self):
return self.title
进入全屏模式 退出全屏模式
在上面的模型Article
中,我们有几个字段,例如title
简单字符字段,content
作为文本字段,因为它将是作为帖子正文的大文本,author
是用户模型的 ForeignKey。我们还有状态,它被定义为一个字符字段,但有几个选择,如draft
或published
,我们可以进一步将此状态修改为公共或私有。但只是保持简单易懂。
此模型的对象引用名称是我们在 dunder 字符串方法中定义的标题。因此,这是一个创建的简单模型,我们现在可以将更改迁移到数据库中以添加表和属性。
python manage.py makemigrations
python manage.py migrate
进入全屏模式 退出全屏模式
这将迁移到数据库,即将 python 模型类转换为数据库表和属性。因此,一旦迁移过程成功完成,我们就可以进入本文的核心部分,即实际设计视图。在下一节中,我们将利用视图中的模型来表示模板上的数据。
创建文章表格
在进入视图部分之前,我们需要一些东西,比如文章表单,它将是一个基于 Django 模型的表单。它将极大地帮助我们创建或更新文章模型的字段。我们可以在一个名为forms.py
的 python 文件中定义一个表单,不必将表单保存在forms.py
中,但是如果您有很多表单和模型,那么组织我们应用程序的组件是一个好习惯。因此,我将在article
应用程序中创建一个名为forms.py
的新文件并定义ArticleForm
。
# article/forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
exclude = (
"created",
"updated",
"author",
)
widgets = {
"title": forms.TextInput(
attrs={
"class": "form-control",
"style": "max-width: 450px; align: center;",
"placeholder": "Title",
}
),
"content": forms.Textarea(
attrs={
"class": "form-control",
"style": "max-width: 900px;",
"placeholder": "Content",
}
),
}
进入全屏模式 退出全屏模式
因此,表单是从 [ModelForm] 继承的,它允许我们基于我们的模型创建表单。因此,我们指定模型名称,在我们的例子中是Article
,而且我们可以有exclude
或fields
个元组。要排除实际表单中的某些字段,只需解析这些属性的元组,如果您只想选择几个属性,您可以指定fields
元组并提及表单的必填字段。
所以,如果我们有很多东西要包含在表单中,我们可以只用exclude
元组指定要排除的属性。如果我们有很多字段要排除,我们可以使用fields
元组来指定在表单中使用哪些属性。
我们举个例子:对于上面的 ArticleForm,如果我们想指定表单中包含的必填字段,那么我们可能会使用下面的fields
元组,其余的将不会在表单字段中呈现。
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = (
"title",
"content",
"status",
)
进入全屏模式 退出全屏模式
两者都可以使用,这取决于您必须在呈现的表单中排除或包含多少字段。
我们还指定了widgets
属性,它可以更好地控制我们需要如何在模板中显示表单。所以我已经指定了它需要渲染的输入类型,比如标题的简单文本输入、内容的文本区域等。这很酷的是它可以通过知道模型中字段的类型来自动设置这些,但是有时它可能有点不受欢迎,主要是复杂的关系和属性。
创建视图
让我们开始创建视图,用于从数据库中创建、阅读、更新和删除文章。我将使用基于函数的视图,因为我们正在了解如何集成 HTMX 和 Django 的流程,因此我们需要更深入地了解流程的实际流程。
创建视图
因此,创建文章似乎是开始的好方法。我们可以创建一个简单的基于函数的视图,它最初会加载一个空的ArticleForm
,如果请求是GET
,我们将在create.html
模板中呈现表单。如果请求是POST
,这将在我们提交表单之后,我们将验证表单并将当前用户附加为文章的作者,并保存将创建文章记录的实例,并且此对象将呈现到详细信息模板。
from django.shortcuts import render
from .models import Article
from .forms import ArticleForm
def createArticle(request):
form = ArticleForm()
context = {
'form': form,
}
return render(request, 'articles/create.html', context)
进入全屏模式 退出全屏模式
呈现表单
我们正在创建一个ArticleForm
的空实例并在模板中呈现它。因此,这将在create.html
模板中呈现空表单。
<!-- templates/articles/create.html -->
{% extends 'base.html' %}
{% block body %}
<div hx-target="this" hx-swap="outerHTML">
<form>
{% csrf_token %}
{{ form.as_p }}
<button hx-post="." class="btn btn-success"
type="submit">Save</button>
</form>
</div>
{% endblock %}
进入全屏模式 退出全屏模式
现在,我们从基本模板继承并在 HTML 中创建一个带有{{ form}}
的表单标签来呈现表单字段,我们终于有了button
元素来提交表单。我们使用了hx-post
属性。更多关于这一点的信息。所以,这是我们创建一个用于呈现文章表单的模板。
我们这里使用了hx-post
属性,它会向当前hx-post="."
表示的URL
发送POST
请求。您可能已经注意到div
属性,即hx-target
和hx-swap
,所以这些是 htmx 库提供的用于控制的众多属性中的一部分所提出请求的反应性。hx-target
允许我们指定将呈现数据的元素或标签。hx-swap
用于指定目标 DOM,如innerHTML
、outerHTML
等。您可以在htmx 文档上查看各种选项。通过将hx-swap
指定为outerHTML
,我们是说将整个元素替换为来自请求的传入内容,我们将使用附近的请求触发器发送该内容。
我们需要将视图映射到 URL,以便更好地了解请求和解析的内容。
我们将创建一个create/
路由并将其绑定到名称为article-create
的createArticle
视图。
# article/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('create/', views.createArticle, name='article-create'),
]
进入全屏模式 退出全屏模式
该 URL 将映射到项目中的全局 URL,在这里我们可以简单地为article
应用程序中的 URL 指定前缀并包含这些 URL。
# htmx_blog/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('user/', include('user.urls'), name='auth'),
path('', include('article.urls'), name='home'),
]
进入全屏模式 退出全屏模式
随意添加任何其他 URL 模式,例如,文章应用程序位于/
即127.0.01.:8000/
,您可以通过添加path('article/', include('article.urls'))
添加任何其他名称,例如127.0.0.1:8000/article/
。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--x09upZKL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https:// res.cloudinary.com/techstructive-blog/image/upload/v1659252089/blog-media/django-htmx-create-view.png)
所以,最后,我们向127.0.0.1:8000/create/
发送GET
请求,这将输出表单。由于我们在表单内的按钮中嵌入了POST
请求,因此我们会将POST
请求发送到相同的 URL ->127.0.0.1:8000/create/
。
提交表格
让我们在创建视图中处理POST
请求。
from django.shortcuts import render
from .models import Article
from .forms import ArticleForm
def createArticle(request):
form = ArticleForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.instance.author = request.user
article = form.save()
return render(request, 'articles/detail.html', {'article': article})
context = {
'form': form,
}
return render(request, 'articles/create.html', context)
进入全屏模式 退出全屏模式
简单解释
-
使用请求数据创建 ArticleForm 的表单实例或为空 ->
ArticleForm(request.POST or None)
-
如果是 POST 请求,验证并创建文章,在
detail.html
模板中渲染文章对象。 -
如果是 GET 请求,则在
create.html
中渲染空表单
视图中有一些变化,我们没有将表单初始化为空,即ArticleForm()
,而是使用ArticleForm(request.POST or None)
进行初始化。这基本上意味着,如果我们在request.POST
字典中有内容,我们将使用该数据初始化表单,否则空表单实例。
接下来,我们检查请求是否为POST
,如果是,则检查表单是否有效,即表单字段不为空,或者是否满足对模型属性的任何其他约束。如果表单数据有效,我们将作者附加为当前登录的用户/发送请求的用户。最后,我们保存表单,然后在数据库中创建文章记录。然后我们在尚未创建的detail.html
模板中渲染创建的文章。
因此,htmx-post
属性起作用了,它会向同一个 URL 即127.0.0.1:8000/create
发送一个 post 请求,这将再次触发视图createArticle
,这次我们将有request.POST
个数据。因此,我们将验证并保存表单。
详细视图
详细视图用于查看文章的详细信息。这将在文章创建或更新后呈现。这很简单,我们需要一篇文章的id
或primary key(pk)
,并在模板中渲染文章的title
和content
。
我们将主键与请求一起作为参数传递给视图,pk
将通过 URL 传递。我们获取 id 为解析后的pk
的 Article 对象,最后用 article 对象渲染detail.html
模板。可以从模板访问context['article']
以呈现特定属性,例如title
、content
等。
# article/views.py
def detailArticle(request, pk):
article = Article.objects.get(id=pk)
context = {'article': article}
return render(request, 'articles/detail.html', context)
进入全屏模式 退出全屏模式
我们现在可以将视图绑定到 URL 并将所需的参数pk
解析到视图。
from django.urls import path
from . import views
urlpatterns = [
path('create/', views.createArticle, name='article-create'),
path('<int:pk>', views.detailArticle, name='article-detail'),
]
进入全屏模式 退出全屏模式
我们已经将pk
解析为int
到 URL 参数,因此对于 idu003d4 的文章,URL 将为127.0.0.1:8000/4/
。
我们需要创建用于从detailArticle
视图渲染上下文的模板。因此,我们在templates/articles
文件夹中创建detail.html
。我们继承了基础模板,并使用换行模板过滤器渲染article.title
和article.content
,以便正确显示内容。
<!-- templates/articles/detail.html -->
{% extends 'base.html' %}
{% block body %}
<div id="article-card">
<h2>{{ article.title }}
<p>{{ article.content|linebreaks|safe }}</p>
<div>
{% endblock %}
进入全屏模式 退出全屏模式
[](https://res.cloudinary.com/practicaldev/image/fetch/s--R-kxIYTW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res .cloudinary.com/techstructive-blog/image/upload/v1659252227/blog-media/django-htmx-detail-view.png)
所以,我们现在可以使用createArticle
视图以及detailArticle
视图,这两者都配置正确,所以(CR)或 CRUD 完成。我们可以添加listArticle
来列出所有作者(登录用户)的文章。
列表视图
文章的列表视图与详细视图非常相似,因为它将返回文章列表而不是单个文章。
所以在listArticle
视图中,我们将返回所有以作者为发送请求的用户/登录用户的文章。我们将此对象列表解析为base.html
或articles/list.html
到模板中。
# article/views.py
def listArticle(request):
articles = Article.objects.filter(author=request.user.id)
context = {
'articles': articles,
}
return render(request, 'base.html', context)
进入全屏模式 退出全屏模式
我们将为此添加 URL 路由作为127.0.0.1:8000/
上的/
路由,这是文章应用程序的基本 URL,并且是listArticle
视图的路由。因此,我们将在主页上显示文章列表。
# article/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('<int:pk>', views.detailArticle, name='article-detail'),
path('create/', views.createArticle, name='article-create'),
path('', views.listArticle, name='article-list'),
]
进入全屏模式 退出全屏模式
让我们为列表视图创建模板,该模板将遍历文章并显示相关数据,如标题和文章链接。
<!-- templates/articles/list.html -->
<ul id="article-list">
{% for article in articles %}
<li>
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">{{ article.title }}</h5>
<p class="card-text">{{ article.content|truncatewords:5 }}</p>
<a href="{% url 'article-detail' article.id %}" class="card-link">Read more</a>
</div>
</div>
</li>
{% endfor %}
</ul>
进入全屏模式 退出全屏模式
我们使用truncatewords:5
模板过滤器只显示文章的内容,直到前 5 个单词,因为它只是一个列表视图,我们不想在这里显示文章的每个细节。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--G2aTVGer--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary .com/techstructive-blog/image/upload/v1659252293/blog-media/django-htmx-list-view.png)
我们可以使用这个模板在base.html
文件中进行渲染。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTMX Blog</title>
{% load static %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://unpkg.com/htmx.org@1.8.0"></script>
</head>
<body hx-target="this" hx-swap="outerHTML" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<nav>
<h2>HTMX Blog</h2>
<div class="navbar">
{% if user.is_authenticated %}
<a class="nav-item nav-link" href="{% url 'article-list' %}"><button class="btn btn-link">Home</button></a>
<a class="nav-item nav-link" href="{% url 'logout' %}"><button class="btn btn-link">Logout</button></a>
{% else %}
<a class="nav-item nav-link" href="{% url 'login' %}"><button class="btn btn-link">Login</button></a>
<a class="nav-item nav-link" href="{% url 'register' %}"><button class="btn btn-link">Register</button></a>
{% endif %}
</div>
</nav>
{% block body %}
<a href="{% url 'article-create' %}"><button class="btn btn-success" >Create</button></a>
{% include 'articles/list.html' %}
{% endblock %}
</body>
</html>
进入全屏模式 退出全屏模式
我们现在已经在主页上包含了list.html
模板,并且还添加了create
按钮作为article-create
URL 的链接。
删除视图
对于删除文章,我们将简单地依赖 htmx 发送请求,根据该请求,我们将删除当前文章并呈现更新的文章列表。
对于deleteArticle
视图,我们将接受两个参数,默认情况下,请求是基于 Django 函数的视图,主键是pk
。我们将再次从 URL 解析pk
。我们将删除文章对象并获取最新的文章列表。最后,在我们的列表视图基本模板中呈现更新的文章列表。
# article/views.py
def deleteArticle(request, pk):
Article.objects.get(id=pk).delete()
articles = Article.objects.filter(author=request.user)
context = {'article': articles}
return render(request, "base.html", context)
进入全屏模式 退出全屏模式
我们将deleteArticle
添加到 URL 模式中,并将其命名为article-delete
,URL 为delete/<int:pk>
。这将允许我们向 URL127.0.0.1:8000/delete/4
发送请求以删除 id 为4
的文章。
# article/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.listArticle, name='article-list'),
path('<int:pk>', views.detailArticle, name='article-detail'),
path('create/', views.createArticle, name='article-create'),
path('delete/<int:pk>', views.deleteArticle, name='article-delete'),
]
进入全屏模式 退出全屏模式
在删除视图中,模板很重要,因为我们希望将请求适当地发送到定义的 URL。为此,我们将有一个表单,但它不会有任何输入,因此只有一个指示删除当前文章的按钮。我们将添加hx-delete
属性作为deleteArticle
视图的 URL。与文章的ID。这将向article-delete
URL 发送一个请求,该 URL 将依次触发具有给定 id 的视图并删除文章。
我们添加了hx-confirm
属性,用于显示删除文章的确认弹出窗口。如您所见,我们添加了一个小脚本,用于将csrf_token
添加到 HTML 中,这对于提交具有有效CSRFToken
的表单很重要。
<!-- templates/article/delete.html -->
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
<div >
<form method="post" >
{% csrf_token %}
<button class="btn btn-danger"
hx-delete="{% url 'article-delete' article.id %}"
hx-confirm="Are you sure, You want to delete this article?"
type="submit">
Delete
</button>
</form>
</div>
进入全屏模式 退出全屏模式
您是否有类似我们如何访问article.id
的问题?我们没有从视图中渲染delete.html
模板,因此没有要传递的上下文。我们会将这个片段包含在详细视图模板中,以便可以选择删除当前文章。
我们将修改articles/detail.html
模板并包含delete.html
模板。这包括简单地在指定位置添加 HTML 模板。因此,我们基本上会将删除表单注入到详细信息模板中。
{% extends 'base.html' %}
{% block body %}
<div hx-target="this" hx-swap="outerHTML">
<h2>{{ article.title }}
{% include 'articles/delete.html' %}
<p>{{ article.content|linebreaks|safe }}</p>
<div>
{% endblock %}
进入全屏模式 退出全屏模式
因此,我们将有一个很好的选择来删除详细部分中的文章,这可以放在任何地方,但请记住,我们需要在 div 中添加hx-target="this"
和hx-swap="outerHTML"
以便在发出请求后正确交换 HTML 内容.
更新视图
我们现在可以进入 CRUD 的最后一部分,即Update
。这将类似于createArticle
,但有一些变化。我们也会将像pk
这样的参数解析到这个视图,因为我们想要更新特定的文章。因此,我们必须从 URL slug 中获取文章的主键。
在updateArticle
视图中,我们将首先从解析后的主键中获取文章对象。我们这里会有两种请求,一种是用当前文章数据获取form
,下一个请求是PUT
请求,用于实际保存文章中的更改。
第一个请求很简单,因为我们需要使用文章对象的实例来解析表单数据。我们将使用article
的实例调用ArticleForm
,这会将文章的数据加载到准备呈现到模板中的表单中。因此,一旦发送了GET
请求,我们将使用预先填充了文章属性值的表单呈现模板。
# article/views.py
def updateArticle(request, pk):
article = Article.objects.get(id=pk)
form = ArticleForm(instance=article)
context = {
'form': form,
'article': article,
}
return render(request, 'articles/update.html', context)
进入全屏模式 退出全屏模式
我们将在templates/articles/
文件夹中创建一个模板为update.html
,它将有一个用于呈现表单字段的简单表单和一个用于发送PUT
请求的按钮。我们将渲染form
,然后添加一个属性为hx-put
的按钮元素,用于发送PUT
请求以保存对文章记录的更改。我们将在article.id
中解析视图的主键参数。
<!-- templates/articles/update.html -->
<div hx-target="this" hx-swap="outerHTML">
<form>
{% csrf_token %}
{{ form.as_p }}
<button hx-put="{% url 'article-update' article.id %}"
type="submit">Update</button>
</form>
</div>
进入全屏模式 退出全屏模式
我们尚未将updateArticle
链接到 URL。我们将视图updateArticle
添加到名称为article-update
和update/<int:pk
作为 slug 模式的 URL 中。当我们向127.0.0.1:8000/update/4
发送 HTTP 请求以更新 id 为4
的文章时,此 URL 模式将触发updateArticle
。
# article/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.listArticle, name='article-list'),
path('<int:pk>', views.detailArticle, name='article-detail'),
path('create/', views.createArticle, name='article-create'),
path('delete/<int:pk>', views.deleteArticle, name='article-delete'),
path('update/<int:pk>', views.updateArticle, name='article-update'),
]
进入全屏模式 退出全屏模式
这还没有完成,我们还需要处理PUT
请求,即当表单详细信息已被修改并且我们即将保存对表单数据的更改时。因此,我们将检查请求方法的类型。如果是PUT
请求,我们将不得不处理一些事情。
# article/views.py
from django.http import QueryDict
def updateArticle(request, pk):
article = Article.objects.get(id=pk)
if request.method == 'PUT':
qd = QueryDict(request.body)
form = ArticleForm(instance=article, data=qd)
if form.is_valid():
article = form.save()
return render(request, 'articles/detail.html', {'article': article})
form = ArticleForm(instance=article)
context = {
'form': form,
'article': article,
}
return render(request, 'articles/update.html', context)
进入全屏模式 退出全屏模式
在上面的updateArticle
视图中,我们要检查一个PUT
请求,如果我们正在发送一个PUT
请求,则需要从请求对象中加载表单实例。我们使用request.body
来访问请求中发送的数据。从request.body
对象接收的传入数据不是将其解析为表单实例的有效格式,因此我们将使用QueryDict
对其进行解析。这将允许我们将request.body
对象修改为有效的 python 可序列化数据。
因此,我们从django.http
模块导入QueryDict
。我们将数据作为参数解析为QueryDict
并将其存储在变量中。然后我们必须获取ArticleForm
以根据表单详细信息获取数据,因此我们解析实例以及data
参数。实例是文章对象,数据是我们在qd
中存储为QueryDict(request.body)
的接收到的表单数据。这将加载新的表单数据,然后我们可以验证它的表单。
在我们验证表单详细信息后,我们可以保存表单,这将更新文章记录。因此,我们可以在detail
视图中以更新的article
对象作为上下文来渲染更新的文章。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--12MgfH9K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res. cloudinary.com/techstructive-blog/image/upload/v1659252091/blog-media/django-htmx-update-view.png)
因此,这也将设置更新视图,我们现在可以在模板和基于 Django 函数的视图中使用 HTMX 创建、读取、更新和删除文章实例,而无需编写任何 javascript。
总结
我们能够使用 HTMX 在 Django 中创建一个基本的 CRUD 应用程序。我们使用简单的基于函数的视图来演示如何使用 HTMX 和处理来自模板的请求的内部细节。通过创建简单的独立模板,我们可以将它们连接在一起以制作一个功能齐全且响应迅速的网页。 UI 不是很好,但本教程的目的是制作一个准系统 CRUD 应用程序以使用 HTMX 与后端一起工作,因此希望您对如何将 HTMX 集成到 Django 应用程序中有一个很好的概述。
总体而言,HTMX 是一个很棒的库,可用于增强甚至创建新的 Web 应用程序,以使网站具有响应性,而无需编写任何 javascript。
Django HTMX CRUD 应用程序演示 GIF
您可以在htmx-blog GitHub存储库上查看此项目和博客的源代码。
结论
从这篇文章中,我们能够了解 HTMX 的基础知识以及如何将它集成到 Django 应用程序中。希望您喜欢这篇文章,如果您有任何疑问或反馈,请在评论或我的社交手柄上告诉我。感谢您的阅读。快乐编码:)
更多推荐
所有评论(0)