htmx-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

HTMX Patterns for Django

用于 Django 的 HTMX 模式

Core Philosophy

核心理念

  • Server renders HTML, not JSON - HTMX requests return HTML fragments, not data
  • Partial templates for dynamic updates - separate
    _partial.html
    files for HTMX responses
  • Progressive enhancement - pages work without JavaScript, HTMX enhances UX
  • Minimal client-side complexity - let the server do the heavy lifting
  • 服务器渲染 HTML,而不是 JSON——HTMX 请求返回 HTML 片段,而不是数据
  • 使用局部模板进行动态更新——为 HTMX 响应拆分
    _partial.html
    文件
  • 渐进式增强——页面在没有 JavaScript 时也能工作,HTMX 则用于提升用户体验
  • 尽量减少客户端复杂度——让服务器承担主要工作

Critical Hints & Reminders

关键提示与提醒

UX Best Practices

UX 最佳实践

Always include loading indicators
  • Use
    hx-indicator
    to show loading states during requests
  • Users should never wonder if their action worked
  • Example:
    <button hx-get="/data/" hx-indicator="#spinner">Load</button>
Always provide user feedback
  • Use Django messages framework for success/error feedback
  • Return error messages in HTMX responses, not silent failures
  • Show what happened after an action completes
Handle errors gracefully
  • Return proper HTTP status codes (400 for validation errors, 500 for server errors)
  • Render form errors in partial templates
  • Don't swallow exceptions - log and show user-friendly messages
始终包含加载指示器
  • 使用
    hx-indicator
    在请求期间显示加载状态
  • 用户不应怀疑自己的操作是否生效
  • 示例:
    <button hx-get="/data/" hx-indicator="#spinner">Load</button>
始终提供用户反馈
  • 使用 Django messages framework 提供成功或错误反馈
  • 在 HTMX 响应中返回错误消息,不要静默失败
  • 操作完成后展示执行结果
妥善处理错误
  • 返回正确的 HTTP 状态码(验证错误使用 400,服务器错误使用 500)
  • 在局部模板中渲染表单错误
  • 不要吞掉异常——记录日志并显示对用户友好的消息

Django-Specific Patterns

Django 专属模式

Always detect HTMX requests
  • Check
    request.headers.get("HX-Request")
    to detect HTMX requests
  • Return partial templates for HTMX, full page templates otherwise
  • Pattern:
    if request.headers.get("HX-Request"): return render(request, "_partial.html", context)
Always return partials for HTMX
  • HTMX requests should return
    _partial.html
    templates, not full pages with
    base.html
  • Full page responses to HTMX requests break the UX and send duplicate HTML
  • Partials should be self-contained HTML fragments
Always validate request.method
  • Check
    request.method == "POST"
    before processing form data
  • Return proper status codes (405 Method Not Allowed for wrong methods)
CSRF is already configured globally
  • The base template has
    hx-headers
    on
    <body>
    - no need to add CSRF tokens to individual forms
  • All HTMX requests automatically include the CSRF token
始终检测 HTMX 请求
  • 检查
    request.headers.get("HX-Request")
    以检测 HTMX 请求
  • 对 HTMX 请求返回局部模板,否则返回完整页面模板
  • 模式:
    if request.headers.get("HX-Request"): return render(request, "_partial.html", context)
始终为 HTMX 返回局部模板
  • HTMX 请求应返回
    _partial.html
    模板,而不是包含
    base.html
    的完整页面
  • 向 HTMX 请求返回完整页面会破坏用户体验,并发送重复的 HTML
  • 局部模板应该是自包含的 HTML 片段
始终验证 request.method
  • 处理表单数据前检查
    request.method == "POST"
  • 对错误的方法返回正确的状态码(405 Method Not Allowed)
CSRF 已经全局配置
  • 基础模板的
    <body>
    上已配置
    hx-headers
    ——无需在单个表单中添加 CSRF token
  • 所有 HTMX 请求都会自动包含 CSRF token

Template Organization

模板组织

Naming convention
  • Partials:
    _partial.html
    (underscore prefix)
  • Full pages:
    page.html
    (no prefix)
  • Example:
    posts/list.html
    (full page) includes
    posts/_list.html
    (partial)
Structure
  • Full page template extends
    base.html
    and includes partial
  • Partial contains only the dynamic HTML fragment
  • HTMX targets the partial's container div
Keep partials focused
  • Each partial should represent one logical UI component
  • Avoid partials that are too large or do too much
  • Compose larger UIs from multiple smaller partials
命名约定
  • 局部模板:
    _partial.html
    (下划线前缀)
  • 完整页面:
    page.html
    (无前缀)
  • 示例:
    posts/list.html
    (完整页面)包含
    posts/_list.html
    (局部模板)
结构
  • 完整页面模板继承
    base.html
    并包含局部模板
  • 局部模板只包含动态 HTML 片段
  • HTMX 的目标指向局部模板的容器 div
让局部模板保持聚焦
  • 每个局部模板应表示一个逻辑 UI 组件
  • 避免局部模板过大或承担过多职责
  • 由多个较小的局部模板组合出更大的 UI

Django View Patterns

Django 视图模式

HTMX Detection

HTMX 检测

Check the
HX-Request
header to detect HTMX requests:
python
def my_view(request):
    context = {...}

    if request.headers.get("HX-Request"):
        return render(request, "app/_partial.html", context)

    return render(request, "app/full_page.html", context)
检查
HX-Request
请求头以检测 HTMX 请求:
python
def my_view(request):
    context = {...}

    if request.headers.get("HX-Request"):
        return render(request, "app/_partial.html", context)

    return render(request, "app/full_page.html", context)

Form Handling Pattern

表单处理模式

Key points:
  • Validate form normally
  • On success: return partial with new data OR trigger client-side event
  • On error: return partial with form errors
  • Always handle both HTMX and non-HTMX cases
python
def create_view(request):
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            obj = form.save()
            if request.headers.get("HX-Request"):
                return render(request, "app/_item.html", {"item": obj})
            return redirect("app:list")

        # Return form with errors
        if request.headers.get("HX-Request"):
            return render(request, "app/_form.html", {"form": form})
    else:
        form = MyForm()

    return render(request, "app/create.html", {"form": form})
关键点:
  • 正常验证表单
  • 成功时:返回包含新数据的局部模板,或者触发客户端事件
  • 错误时:返回包含表单错误的局部模板
  • 始终同时处理 HTMX 和非 HTMX 情况
python
def create_view(request):
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            obj = form.save()
            if request.headers.get("HX-Request"):
                return render(request, "app/_item.html", {"item": obj})
            return redirect("app:list")

        # Return form with errors
        if request.headers.get("HX-Request"):
            return render(request, "app/_form.html", {"form": form})
    else:
        form = MyForm()

    return render(request, "app/create.html", {"form": form})

Response Headers Reference

响应头参考

HTMX respects special response headers for client-side behavior:
HTMX 会遵循特殊响应头来控制客户端行为:

HX-Trigger

HX-Trigger

Trigger client-side events after response
  • Use case: Update other parts of page after action
  • Example:
    response["HX-Trigger"] = "itemCreated"
  • Template listens:
    <div hx-get="/count/" hx-trigger="itemCreated from:body">
在响应后触发客户端事件
  • 使用场景:操作后更新页面的其他部分
  • 示例:
    response["HX-Trigger"] = "itemCreated"
  • 模板监听:
    <div hx-get="/count/" hx-trigger="itemCreated from:body">

HX-Redirect

HX-Redirect

Client-side redirect
  • Use case: Redirect after successful action
  • Example:
    response["HX-Redirect"] = reverse("app:detail", args=[obj.pk])
客户端重定向
  • 使用场景:操作成功后进行重定向
  • 示例:
    response["HX-Redirect"] = reverse("app:detail", args=[obj.pk])

HX-Retarget / HX-Reswap

HX-Retarget / HX-Reswap

Override hx-target and hx-swap from server
  • Use case: Different targets for success vs error
  • Success:
    response["HX-Retarget"] = "#main"
  • Error: Return partial without changing target (targets the form)
从服务端覆盖
hx-target
和
hx-swap
  • 使用场景:为成功和错误情况使用不同目标
  • 成功:
    response["HX-Retarget"] = "#main"
  • 错误:返回局部模板但不更改目标(目标为表单)

HX-Refresh

HX-Refresh

Force full page refresh
  • Use case: Major state change that affects whole page
  • Example:
    response["HX-Refresh"] = "true"
强制刷新完整页面
  • 使用场景:发生影响整个页面的重大状态变化
  • 示例:
    response["HX-Refresh"] = "true"

Common Pitfalls

常见陷阱

  • Missing loading indicators: Always use
    hx-indicator
    - users click multiple times without feedback
  • Full pages in HTMX responses: Return
    _partial.html
    , not full pages with
    base.html
    - check
    HX-Request
    header
  • Not handling form errors: Always return the form with errors on validation failure, not just the success case
  • Not disabling buttons: Use
    hx-disabled-elt="this"
    to prevent duplicate submissions
  • N+1 queries: HTMX views need
    select_related()
    /
    prefetch_related()
    just like regular views
  • 缺少加载指示器:始终使用
    hx-indicator
    ——否则用户在没有反馈的情况下可能会重复点击
  • 在 HTMX 响应中返回完整页面:应返回
    _partial.html
    ,而不是包含
    base.html
    的完整页面——检查
    HX-Request
    请求头
  • 未处理表单错误:验证失败时始终返回带有错误的表单,而不能只处理成功情况
  • 未禁用按钮:使用
    hx-disabled-elt="this"
    防止重复提交
  • N+1 查询:与常规视图一样,HTMX 视图也需要使用
    select_related()
    /
    prefetch_related()

Integration with Other Skills

与其他 Skill 的集成

  • django-templates: Partial template organization and inheritance patterns
  • django-forms: HTMX form submission and validation
  • django-extensions: Use
    show_urls
    to verify HTMX endpoints
  • pytest-django-patterns: Testing HTMX endpoints and headers
  • systematic-debugging: Debug HTMX request/response issues
  • django-templates:局部模板组织与继承模式
  • django-forms:HTMX 表单提交与验证
  • django-extensions:使用
    show_urls
    验证 HTMX 端点
  • pytest-django-patterns:测试 HTMX 端点和请求头
  • systematic-debugging:调试 HTMX 请求与响应问题