1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
from __future__ import annotations
import time
from dataclasses import dataclass
from django.core.paginator import Paginator
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from django.views.decorators.http import require_GET, require_http_methods, require_POST
from faker import Faker
from django_htmx.middleware import HtmxDetails
from example.forms import OddNumberForm
# Typing pattern recommended by django-stubs:
# https://github.com/typeddjango/django-stubs#how-can-i-create-a-httprequest-thats-guaranteed-to-have-an-authenticated-user
class HtmxHttpRequest(HttpRequest):
htmx: HtmxDetails
@require_GET
def index(request: HtmxHttpRequest) -> HttpResponse:
return render(request, "index.html")
@require_GET
def favicon(request: HtmxHttpRequest) -> HttpResponse:
return HttpResponse(
(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">'
+ '<text y=".9em" font-size="90">🦊</text>'
+ "</svg>"
),
content_type="image/svg+xml",
)
# CSRF Demo
@require_GET
def csrf_demo(request: HtmxHttpRequest) -> HttpResponse:
return render(request, "csrf-demo.html")
@require_POST
def csrf_demo_checker(request: HtmxHttpRequest) -> HttpResponse:
form = OddNumberForm(request.POST)
if form.is_valid():
number = form.cleaned_data["number"]
number_is_odd = number % 2 == 1
else:
number_is_odd = False
return render(
request,
"csrf-demo-checker.html",
{"form": form, "number_is_odd": number_is_odd},
)
# Error demo
@require_GET
def error_demo(request: HtmxHttpRequest) -> HttpResponse:
return render(request, "error-demo.html")
@require_GET
def error_demo_trigger(request: HtmxHttpRequest) -> HttpResponse:
_ = 1 / 0
return render(request, "error-demo.html") # unreachable
# Middleware tester
# This uses two views - one to render the form, and the second to render the
# table of attributes.
@require_GET
def middleware_tester(request: HtmxHttpRequest) -> HttpResponse:
return render(request, "middleware-tester.html")
@require_http_methods(["DELETE", "POST", "PUT"])
def middleware_tester_table(request: HtmxHttpRequest) -> HttpResponse:
return render(
request,
"middleware-tester-table.html",
{"timestamp": time.time()},
)
# Partial rendering example
# This dataclass acts as a stand-in for a database model - the example app
# avoids having a database for simplicity.
@dataclass
class Person:
id: int
name: str
faker = Faker()
people = [Person(id=i, name=faker.name()) for i in range(1, 235)]
@require_GET
def partial_rendering(request: HtmxHttpRequest) -> HttpResponse:
# Standard Django pagination
page_num = request.GET.get("page", "1")
page = Paginator(object_list=people, per_page=10).get_page(page_num)
# The htmx magic - render just the `#table-section` partial for htmx
# requests, allowing us to skip rendering the unchanging parts of the
# template.
template_name = "partial-rendering.html"
if request.htmx:
template_name += "#table-section"
return render(
request,
template_name,
{
"page": page,
},
)
|