简介

使用 javascript 编写 Ajax 请求的日子已经一去不复返了,只需向 HTML 内容标签添加一些参数,您就可以准备向后端发送请求了。所以,我们要回到过去,纠正我们对 API 和客户端/服务器端渲染的看法。我们指的是超媒体模型用于利用服务器端的数据处理。让我们用HTMX了解这种古老但革命性的开发方法。

是的,HTMX 可以直接在 HTML 中用于 API/服务器端调用。我们将通过创建一个基本的 CRUD 应用程序来探索 HTMX 的基础。

什么是HTMX?

可能出现的第一个问题是什么以及为什么 HTMX? Htmx 是一个很棒的库,它是一个 javascript 库,但是等等。它是一个 javascript 库,旨在允许我们编写更少或根本不编写 javascript。它可以作为一种发送 AJAX 请求的方式,而无需您编写任何 javascript。它直接从 HTML 使用本机浏览器功能。

因此,我们可以使用 HTMX 在 Django 应用程序中创建交互式模板。我们可以使用简单的 HTML 属性(如hx-gethx-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文件夹,其中cssjsimages作为较大项目的子文件夹。

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 模型,其中包含一些参数,例如titlecontentauthor等。

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。我们还有状态,它被定义为一个字符字段,但有几个选择,如draftpublished,我们可以进一步将此状态修改为公共或私有。但只是保持简单易懂。

此模型的对象引用名称是我们在 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,而且我们可以有excludefields个元组。要排除实际表单中的某些字段,只需解析这些属性的元组,如果您只想选择几个属性,您可以指定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-targethx-swap,所以这些是 htmx 库提供的用于控制的众多属性中的一部分所提出请求的反应性。hx-target允许我们指定将呈现数据的元素或标签。hx-swap用于指定目标 DOM,如innerHTMLouterHTML等。您可以在htmx 文档上查看各种选项。通过将hx-swap指定为outerHTML,我们是说将整个元素替换为来自请求的传入内容,我们将使用附近的请求触发器发送该内容。

我们需要将视图映射到 URL,以便更好地了解请求和解析的内容。

我们将创建一个create/路由并将其绑定到名称为article-createcreateArticle视图。

# 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/

[Django HTMX 创建视图表单模板](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个数据。因此,我们将验证并保存表单。

详细视图

详细视图用于查看文章的详细信息。这将在文章创建或更新后呈现。这很简单,我们需要一篇文章的idprimary key(pk),并在模板中渲染文章的titlecontent

我们将主键与请求一起作为参数传递给视图,pk将通过 URL 传递。我们获取 id 为解析后的pk的 Article 对象,最后用 article 对象渲染detail.html模板。可以从模板访问context['article']以呈现特定属性,例如titlecontent等。

# 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.titlearticle.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.htmlarticles/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-createURL 的链接。

删除视图

对于删除文章,我们将简单地依赖 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-deleteURL 发送一个请求,该 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-updateupdate/<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 应用程序中。希望您喜欢这篇文章,如果您有任何疑问或反馈,请在评论或我的社交手柄上告诉我。感谢您的阅读。快乐编码:)

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐