大数据

Django - AJAX Requests, HTMX & CSRF Tokens

本教程依赖环境:

  • Django
  • HTMX
  • DaysiUI

可参考教程:

示例:

core/models.py

from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=128)
    description = models.TextField()
    published = models.BooleanField(default=False)

    def __str__(self):
        return self.title

执行:

python manage.py makemigrations
python manage.py migrate

core/admin.py

from django.contrib import admin
from core.models import Article

admin.site.register(Article)

创建django用户登录后台添加几条Article记录

core/views.py

from django.shortcuts import render
from django.views.decorators.http import require_http_methods

from core.models import Article


def article_list(request):
    context = {"articles": Article.objects.all()}
    return render(request, "article_list.html", context)


@require_http_methods(["POST"])
def publish_article(request, pk):
    article = get_object_or_404(Article, pk=pk)
    article.published = True
    article.save()
    return render(request, "article_row.html", {"article": article})

core/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name="index"),
    path('articles/', views.article_list, name="article_list"),
    path('publish_article/<int:pk>/', views.publish_article, name="publish_article"),
]

模板配置:

base.html

{% load django_vite %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django & Vite</title>
    <style>[x-cloak]{display:none}</style>
    {% vite_hmr_client %}
    {% vite_asset 'static/js/main.js' %}
</head>
<body class="p-12 text-4xl" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
    <div class="m-12">
        {% block content %}
        {% endblock %}
    </div>
</body>
</html>

修改内容:

<body class="p-12 text-4xl" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>

article_list.html

{% extends 'base.html' %}

{% block content %}
    <div class="w-6xl">
        <h1 class="text-2xl font-bold mb-4">Articles</h1>
        <div class="overflow-x-auto">
            <table class="table table-zebra w-full">
                <thead>
                <tr>
                    <th class="text-left">Tile</th>
                    <th class="text-left">Description</th>
                    <th class="text-left">Published</th>
                    <th class="text-left">Actions</th>
                </tr>
                </thead>
                <tbody>
                {% for article in articles %}
                    {% include 'article-row.html' %}
                {% endfor %}

                </tbody>
            </table>
        </div>
    </div>
{% endblock %}

article_row.html

<tr>
    <td>{{ article.title }}</td>
    <td>{{ article.description }}</td>
    <td>
        {% if article.published %}
            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current"
                 fill="none" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                      d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
            </svg>
        {% else %}
            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current"
                 fill="none" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                      d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
            </svg>
        {% endif %}
    </td>
    <td>
        {% if not article.published %}
            <button class="btn btn-success publish-button"
                    hx-post="{% url 'publish_article' article.pk %}"
                    hx-target="closest tr"
                    hx-swap="outerHTML"
            >
                Publish
            </button>
        {% endif %}
    </td>
</tr>