### Demo App Setup Instructions Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt Provides commands to install dependencies, set up the database, and run the Django development server for the demo application. ```bash # Install and run the demo cd code pip install -r requirements.txt python manage.py migrate python manage.py runserver # Visit http://127.0.0.1:8000/ ``` -------------------------------- ### Local Development Setup Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/README.rst Steps to set up the project locally, including installing requirements, migrating the database, and running the development server. ```bash cd code pip install -r requirements.txt python manage.py migrate python manage.py runserver ``` -------------------------------- ### Install django-render-block Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/README.rst Install the django-render-block package to enable advanced templating features for htmx patterns. ```bash pip install django-render-block ``` -------------------------------- ### Install Django Form Dependencies Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Install necessary Python packages for CSS compression, Sass compilation, and form widget manipulation in Django. ```shell pip install django-compressor django-libsass django-widget-tweaks ``` -------------------------------- ### Create a New GET Request from an Existing Request Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/view_restart.rst This utility function creates a new HttpRequest object with the method set to GET and an empty POST data dictionary, based on an existing request. This is useful for re-rendering views without carrying over POST data. ```python import copy from django.http.request import HttpRequest, QueryDict def make_get_request(request: HttpRequest) -> HttpRequest: """ Returns a new GET request based on passed in request. """ new_request = copy.copy(request) new_request.POST = QueryDict() new_request.method = "GET" return new_request ``` -------------------------------- ### Single Django View for Multiple Actions (POST + GET) Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This Python view combines GET rendering and POST action handling using `@for_htmx(use_block_from_params=True)`. It skips redirects for HTMX requests, allowing for seamless updates without a full page reload. ```python # views/actions.py from django.http import HttpRequest, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.template.response import TemplateResponse from htmx_patterns.models import Monster from htmx_patterns.utils import for_htmx, is_htmx @for_htmx(use_block_from_params=True) def multiple_actions(request: HttpRequest, monster_id: int): monster = get_object_or_404(Monster.objects.all(), id=monster_id) if request.method == "POST": if "kick" in request.POST: monster.kick() # sets is_happy=False elif "hug" in request.POST: monster.hug() # sets is_happy=True if not is_htmx(request): return HttpResponseRedirect("") # normal POST/redirect/GET for non-htmx return TemplateResponse(request, "multiple_actions.html", {"monster": monster}) ``` -------------------------------- ### HTML for Monster List Item Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/view_restart.rst Example HTML structure for rendering a monster item in a list, including a checkbox and the monster's name. This is used within the Django template. ```html {% for monster in happy_monsters %}
{% endfor %} ... ``` -------------------------------- ### Security Consideration: Sensitive Information in Partials Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/inline_partials.rst This example highlights a potential security risk where sensitive information, protected by a user permission check, might be exposed if a client can request a partial template response. ```html+django {% if user.can_view_sensitive_info %} ``` -------------------------------- ### Partial Template for Pagination Controls Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/separate_partials_single_view.rst This HTMX-enabled partial template displays a list of items and a 'Load more' link that triggers an HTMX GET request. ```html+django {% for monster in page_obj %}

{{ monster.name }}

{% endfor %} {% if page_obj.has_next %}

Load more

{% else %}

That's all of them!

{% endif %} ``` -------------------------------- ### HTMX Configuration for Monster List Update Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst Configures an HTML element to listen for the 'monsterCreated' event. Upon receiving this event, it triggers a GET request to the current URL, targeting and replacing the '#monster-list' element with the updated content, specifically rendering the 'monster-list' block from the server. ```html {% block monster-list %}
{% for monster in monsters %} ... {% endfor %}
{% endblock %} ``` -------------------------------- ### Handle HTMX Request and Re-render View Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/view_restart.rst This snippet checks if the request is an HTMX request. If it is, it re-renders the monster list using a modified GET request. Otherwise, it performs an HTTP redirect. ```python for monster in sad_monsters: if f"sad_monster_{monster.id}" in request.POST: monster.hug() ### New code here: ### if is_htmx(request): return _monster_list(make_get_request(request)) return HttpResponseRedirect("") return TemplateResponse( request, "monster_list.html", { "happy_monsters": happy_monsters, "sad_monsters": sad_monsters, }, ) ``` -------------------------------- ### Triggering a Modal Dialog with HTMX Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst Use this button to trigger the loading of modal dialog content. It specifies the HTMX GET request, the target element, and the swap method. ```html ``` -------------------------------- ### Django View for Creating Monsters with HTMX Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This view handles both GET requests to display the form and POST requests to create a new monster. It uses `Hx-Trigger` headers to signal the client to close the modal and refresh the parent list. ```python import json from django.forms import ModelForm from django.http import HttpRequest, HttpResponse from django.template.response import TemplateResponse from htmx_patterns.models import Monster from htmx_patterns.utils import for_htmx class CreateMonsterForm(ModelForm): class Meta: model = Monster fields = ["name", "is_happy"] @for_htmx(use_block_from_params=True) def main(request: HttpRequest): # Parent page; refreshes monster list when 'monsterCreated' event fires return TemplateResponse(request, "modals_main.html", { "monsters": Monster.objects.all().order_by("name"), }) @for_htmx(use_block_from_params=True) def create_monster(request: HttpRequest): if request.method == "POST": form = CreateMonsterForm(request.POST) if form.is_valid(): monster = form.save() # Signal the client: close the dialog, refresh parent monster list return HttpResponse( headers={"Hx-Trigger": json.dumps({ "closeModal": True, "monsterCreated": monster.id, })} ) else: form = CreateMonsterForm() return TemplateResponse(request, "modals_create_monster.html", {"form": form}) ``` -------------------------------- ### Django View for Modal Creation with HTMX Support Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst This Django view handles both GET requests to display the modal form and POST requests for form submission and validation. It uses the `@for_htmx(use_block_from_params=True)` decorator to enable specific HTMX functionality, such as returning only a block of HTML. ```python @for_htmx(use_block_from_params=True) def create_monster(request: HttpRequest): if request.method == "POST": ``` -------------------------------- ### Parent Page Monster List Block with HTMX Event Listener Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This Django block displays a list of monsters and listens for the 'monsterCreated' event. Upon receiving the event, it triggers a GET request to refresh its own content. ```html+django {% block monster-list %}
{% for monster in monsters %}

{{ monster.name }}

{% endfor %}
{% endblock %} ``` -------------------------------- ### HTMX View Restart Pattern in Django Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This pattern restarts a view with fresh database state for HTMX requests. It converts POST requests to GET requests internally to re-render the view without an additional HTTP round trip. ```python from django.http import HttpRequest, HttpResponseRedirect from django.template.response import TemplateResponse from django.utils.functional import partition from htmx_patterns.models import Monster from htmx_patterns.utils import for_htmx, is_htmx, make_get_request @for_htmx(use_block_from_params=True) def view_restart(request: HttpRequest): return _view_restart(request) def _view_restart(request: HttpRequest, *, selected_happy_monsters=None, selected_sad_monsters=None): monsters = Monster.objects.all() sad_monsters, happy_monsters = partition(lambda m: m.is_happy, monsters) if request.method == "POST": selected_happy = [m for m in happy_monsters if f"happy_monster_{m.id}" in request.POST] selected_sad = [m for m in sad_monsters if f"sad_monster_{m.id}" in request.POST] if "kick" in request.POST: for m in selected_happy: m.kick() selected_happy = [] if "hug" in request.POST: for m in selected_sad: m.hug() selected_sad = [] if is_htmx(request): # Internal restart: re-run from top with fresh DB state, no extra HTTP request return _view_restart( make_get_request(request), # converts POST → GET copy selected_happy_monsters=selected_happy, selected_sad_monsters=selected_sad, ) return HttpResponseRedirect("") return TemplateResponse(request, "view_restart.html", { "happy_monsters": happy_monsters, "sad_monsters": sad_monsters, "selected_happy_monsters": selected_happy_monsters or [], "selected_sad_monsters": selected_sad_monsters or [], }) ``` -------------------------------- ### Django Form View with HTMX Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst A standard Django form view that handles both GET (displaying the form) and POST (processing form submission) requests. It uses TemplateResponse for rendering and redirects upon successful creation. ```python from django.shortcuts import render, redirect from django.template.response import TemplateResponse from django.contrib import messages def create_monster(request): if request.method == "POST": form = CreateMonsterForm(request.POST) if form.is_valid(): monster = form.save() messages.info(request, f"Monster {monster.name} created. You can make another.") return redirect(".") else: form = CreateMonsterForm() return TemplateResponse(request, "create_monster.html", {"form": form}) ``` -------------------------------- ### HTMX View Restart Pattern Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/view_restart.rst Refactored Django view using the 'view restart' pattern for HTMX. The main view calls a helper function, passing a modified request object to simulate a GET request and avoid infinite loops. ```python @for_htmx(use_block_from_params=True) def monster_list(request: HttpRequest): return _monster_list(request) def _monster_list(request: HttpRequest): monsters = Monster.objects.all() sad_monsters, happy_monsters = partition(lambda m: m.is_happy, monsters) if request.method == "POST": if "kick" in request.POST: for monster in happy_monsters: if f"happy_monster_{monster.id}" in request.POST: monster.kick() if "hug" in request.POST: ``` -------------------------------- ### Download Bulma CSS Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Download the Bulma CSS framework and save it to your project's static files. Saving with an SCSS extension is recommended for pre-processing. ```shell curl https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.css > my_app/static/vendor/bulma.scss ``` -------------------------------- ### Django View for Initial Page Load Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/separate_partials.rst This view renders the main template, passing the list of monsters to be displayed. It serves the full page initially. ```python def toggle_with_separate_partials(request): return TemplateResponse( request, "toggle_with_separate_partials.html", { "monsters": Monster.objects.all(), }, ) ``` -------------------------------- ### Configure Django Settings for CSS Compression Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Update Django's settings to include required apps, configure static file finders, and set up the SCSS precompiler for django-compressor. ```python INSTALLED_APPS = [ ... "django.forms", "compressor", "widget_tweaks", ] STATICFILES_FINDERS = ( "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", # other finders.. "compressor.finders.CompressorFinder", ) STATIC_ROOT = BASE_DIR / "_static" COMPRESS_ENABLED = True COMPRESS_PRECOMPILERS = [("text/x-scss", "django_libsass.SassCompiler")] ``` -------------------------------- ### Key Dependencies for Demo App Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt Lists essential Python packages required for the Django HTMX patterns demo application, including Django itself and specific HTMX-related libraries. ```python # requirements.txt (key dependencies) # Django>=4.1 # django-render-block>=0.9.1 ← required for inline partials # django-compressor>=4.3 ← optional, for CSS/JS bundling # django-libsass>=0.9 ← optional, for Sass/SCSS # django-widget-tweaks>=1.4.12 ← optional, for form template filters ``` -------------------------------- ### Include HTMX from Local Static Files Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/base_template.rst Include HTMX by downloading the `htmx.min.js` file and serving it via Django's static files. This approach is useful for offline development or when CDN is not preferred. ```html+django {% load static %} ``` -------------------------------- ### HTMX Trigger Button for Adding a Monster Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This HTML button uses HTMX attributes to trigger a GET request to the monster creation endpoint, targeting the body to append the modal. ```html ``` -------------------------------- ### Load More with Inline Partial Block Selection (HTML/Django) Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/inline_partials.rst This snippet demonstrates how to implement a 'load more' functionality using HTMX. It utilizes `hx-vals` to specify which template block should be rendered and returned by the server, allowing the client to control the partial update. The target element is `#paging-area` and the swap strategy is `outerHTML`. ```html+django {% extends "base.html" %} {% block body %}

List of monsters

{% if page_obj.paginator.count == 0 %}

We have no monsters!

{% else %} {% block page-and-paging-controls %} {% for monster in page_obj %}

{{ monster.name }}

{% endfor %} {% if page_obj.has_next %}

Load more

{% else %}

That's all of them!

{% endif %} {% endblock %} {% endif %} {% endblock %} ``` -------------------------------- ### Django View for Inline Partial Rendering (Basic) Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/inline_partials.rst This view checks for HTMX requests and uses `render_block_to_string` to return only the specified template block. Otherwise, it renders the full template. ```python from render_block import render_block_to_string def paging_with_inline_partials(request): template_name = "paging_with_inline_partials.html" context = { "page_obj": get_page_by_request(request, Monster.objects.all()), } if request.headers.get("Hx-Request", False): rendered_block = render_block_to_string( template_name, "page-and-paging-controls", context=context, request=request ) return HttpResponse(content=rendered_block) return TemplateResponse( request, template_name, context, ) ``` -------------------------------- ### View for Improved Inline Partial Rendering (Python) Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/inline_partials.rst This Python view integrates with the HTMX 'use_block' pattern by using a custom decorator `for_htmx` with `use_block_from_params=True`. This allows the view to dynamically render specific template blocks based on parameters sent with the HTMX request, simplifying the view logic. ```python @for_htmx(use_block_from_params=True) def paging_with_inline_partials_improved_lob(request): return TemplateResponse( request, "paging_with_inline_partials_improved_lob.html", { "page_obj": get_page_by_request(request, Monster.objects.all()), }, ) ``` -------------------------------- ### HTMX Field Row Template Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This Django template snippet renders a form field row with HTMX attributes for on-the-fly validation. It triggers a GET request to the current URL on `focusout` to validate the specific field. ```html+django {# forms/bulma/field_row.html (simplified) #}
{{ field.label_tag }}
{{ field|add_class:error_class|attr:"hx-preserve:true" }}
{% if errors %}
{{ errors }}
{% endif %}
``` -------------------------------- ### HTMX Form Validation View Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This Django view uses the `htmx_form_validate` decorator to enable per-field validation on form submission. It handles both GET requests for initial form display and POST requests for validation and saving. ```python from htmx_patterns.form_utils import htmx_form_validate from htmx_patterns.form_renderers import BulmaFormMixin from django.forms import ModelForm from django.forms.fields import DateField from django.forms.widgets import DateInput from htmx_patterns.models import Monster from django.template.response import TemplateResponse from django.shortcuts import redirect from django.contrib import messages class CreateMonsterForm(BulmaFormMixin, ModelForm): date_of_birth = DateField(initial=None, widget=DateInput(attrs={"type": "date"})) do_htmx_validation = True # opt-in flag; template reads this to add hx- attributes class Meta: model = Monster fields = ["name", "is_happy", "date_of_birth", "type"] @htmx_form_validate(form_class=CreateMonsterForm) def form_validation(request): if request.method == "POST": form = CreateMonsterForm(request.POST) if form.is_valid(): monster = form.save() messages.info(request, f"Monster {monster.name} created.") return redirect(".") else: form = CreateMonsterForm() return TemplateResponse(request, "form_validation.html", {"form": form}) ``` -------------------------------- ### HTMX Form Validation Decorator for Django Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This decorator enables real-time, per-field validation for Django forms when using HTMX. It intercepts GET requests with a `_validate_field` parameter to return only the HTML for the updated field row. ```python from functools import wraps from django.forms import Form from django.http import HttpResponse def htmx_form_validate(*, form_class: type): def decorator(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): if ( request.method == "GET" and "Hx-Request" in request.headers and (field_name := request.GET.get("_validate_field")) ): form = form_class(request.GET) form.is_valid() # run all validators; we'll render only one field return HttpResponse(render_single_field_row(form, field_name)) return view_func(request, *args, **kwargs) return wrapper return decorator ``` -------------------------------- ### HTMX Form Validation Decorator Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst A decorator that intercepts GET requests with the `Hx-Request` header and a `_validate_field` parameter. It performs validation using the specified form class and returns the rendered single field row with errors. ```python def htmx_form_validate(*, form_class: type): """ Instead of a normal view, just do htmx validation using the given form class, for a single field and return the single div that needs to be replaced. Normally the form class will be the same class used in the view body. """ def decorator(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): if ( request.method == "GET" and "Hx-Request" in request.headers and (htmx_validation_field := request.GET.get("_validate_field", None)) ): form = form_class(request.GET) form.is_valid() # trigger validation return HttpResponse(render_single_field_row(form, htmx_validation_field)) return view_func(request, *args, **kwargs) return wrapper return decorator ``` -------------------------------- ### Displaying Paged List with Partial Controls Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/paging_with_separate_partials.html This template extends a base template and displays a list of monsters. It conditionally includes a partial template for paging controls if monsters exist. Use this pattern to keep your main templates clean and your paging logic reusable. ```django {% extends "base.html" %} {% block body %} List of monsters ================ {% if page_obj.paginator.count == 0 %} We have no monsters! {% else %} {% include "_page_and_paging_controls.html" %} {% endif %} See also: [create monsters page]({% url 'simple_post_form' %}) {% endblock %} ``` -------------------------------- ### CSS for Dialog Styling and Transitions Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst Provides CSS rules for styling HTML dialog elements, including overriding default behaviors, setting dimensions and positioning, and applying transitions for opacity to create fade-in effects. It also styles the dialog backdrop. ```css dialog { /* Override some builtins that limit us: */ max-height: 100vh; max-width: 100vw; /* Positioning */ box-sizing: border-box; width: calc(100vw - 40px); height: calc(100vh - 40px); top: 20px; left: 20px; position: fixed; margin: 0; /* Styling */ border: 0; border-top: 2px solid #888; padding: 20px; /* Fade in: */ display: flex; /* for some reason, display: block disables the transition. */ flex-direction: column; opacity: 0; transition: opacity 0.15s; pointer-events: none; /* necessary or the main page becomes inaccessible after closing dialog */ } dialog[open] { opacity: 1; pointer-events: inherit; } dialog::backdrop { background-color: #0008; } ``` -------------------------------- ### Access HTMX Headers in Django View Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/headers.rst Check for the 'Hx-Request' header to determine if the request was sent by HTMX. If it is an HTMX request, extract the 'Hx-Current-URL' header to get the originating URL. Otherwise, return a standard template response. ```python def headers_demo(request): if request.headers.get("Hx-Request", False): current_url = request.headers["Hx-Current-URL"] return HttpResponse(f"This is a response to a request sent by htmx from {current_url}
") return TemplateResponse(request, "headers.html", {}) ``` -------------------------------- ### Django Template: Displaying a List of Monsters Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/modals_main.html Iterates over a list of 'monsters' and displays their name and happiness status. Includes a fallback for when no monsters are present. ```html {% block monster-list %} {% if monsters %} {% for monster in monsters %} {% endfor %} Name Happy? {{ monster.name }} {{ monster.is_happy|yesno:"Yes,No" }} {% else %} There are no monsters {% endif %} {% endblock %} ``` -------------------------------- ### Django Form Widget Configuration Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Demonstrates how to configure Django form widgets to render specific HTML input types, such as setting 'date_of_birth' to render as ''. This is achieved either in the Meta class or by defining the field directly. ```python from django.forms import ModelForm, DateField, DateInput class CreateMonsterForm(ModelForm): class Meta: fields = [..., "date_of_birth"] widgets = { "date_of_birth": DateInput(attrs={\"type\": \"date\"}), } ``` ```python from django.forms import ModelForm, DateField, DateInput class CreateMonsterForm(ModelForm): date_of_birth = DateField(widget=DateInput(attrs={\"type\": \"date\"})) ``` -------------------------------- ### Base HTML Template with Compressed CSS Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Include Bulma CSS in your base HTML template using Django's compress tags for pre-processing SCSS files. ```html+django {% load static %} {% load compress %} {% compress css %} {% endcompress %} ``` -------------------------------- ### Django Template: Base Structure and Block Inheritance Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/modals_main.html Extends a base template and defines blocks for content insertion. Used for overall page structure. ```html {% extends "base.html" %} {% load static %} {% block extrahead %} {% endblock %} {% block body %} ``` -------------------------------- ### Add htmx attributes for field validation Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Conditionally add htmx attributes to a form field's container div for triggering server-side validation on focus out. This setup avoids validating file inputs and checkboxes and uses the idiomorph extension for swapping. ```html+django
{# etc #}
``` -------------------------------- ### Include htmx and set global hx-headers in base template Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt Include the htmx library via CDN or local static file in your base template. Set `hx-headers` on the `` tag to globally pass the CSRF token for all POST requests. ```html+django {# base.html #} {% load static %} {% block body %}{% endblock %} ``` -------------------------------- ### Include HTMX via CDN in Base Template Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/base_template.rst Use this method to quickly add HTMX to your project by linking to the Content Delivery Network. Ensure you specify a version for stability. ```html+django ... ``` -------------------------------- ### SCSS Import for Bulma Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Import the Bulma CSS framework into your project's SCSS file. Custom styles can be added after the import. ```scss @import "../vendor/bulma.scss"; // Our styles here … body { padding: 1rem; } // … ``` -------------------------------- ### Conditional Block Rendering with `for_htmx` Decorator Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/inline_partials.rst Demonstrates how to use multiple `for_htmx` decorators to conditionally render different blocks based on the `Hx-Target` header, useful for pages with multiple HTMX interactions. ```python @for_htmx(if_hx_target="search-results", use_block="search-result-block") @for_htmx(if_hx_target="paging-controls", use_block="page-and-paging-controls") def my_view(request): ... ``` -------------------------------- ### Use `for_htmx` decorator with `use_block_from_params` Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This option for the `for_htmx` decorator allows the template to declare the block to be rendered via `hx-vals`, improving the locality of behavior. It's recommended for better code organization. ```python # views/partials.py from django.template.response import TemplateResponse from htmx_patterns.utils import for_htmx from htmx_patterns.models import Monster from django.core.paginator import Paginator def get_page_by_request(request, queryset, paginate_by=6): return Paginator(queryset, per_page=paginate_by).get_page( request.GET.get("page") ) # Option 3: let the template declare the block via hx-vals (best locality of behaviour) @for_htmx(use_block_from_params=True) def paging_with_inline_partials_lob(request): return TemplateResponse( request, "paging_with_inline_partials_improved_lob.html", {"page_obj": get_page_by_request(request, Monster.objects.all())}, ) ``` -------------------------------- ### HTMX Load More Button Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/_page_and_paging_controls.html Provides a 'Load more' button that uses HTMX to fetch the next page of results. Includes logic to hide the button when there are no more pages. ```html {% if page_obj.has_next %} [Load more](?page={{ page_obj.next_page_number }}) {% else %} That's all of them! {% endif %} ``` -------------------------------- ### HTML Form for Multiple Actions (No HTMX) Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/actions.rst This HTML form uses buttons with different 'name' attributes to trigger distinct actions on the server. The 'action' attribute is set to an empty string to submit to the current URL. ```html
{% csrf_token %} {% if monster.is_happy %}

{{ monster.name }} is happy.

{% else %}

{{ monster.name }} is sad.

{% endif %}
``` -------------------------------- ### JavaScript to Show Modal on Load Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst This vanilla JavaScript listens for the 'htmx:afterSettle' event and automatically calls the `.showModal()` method on any dialog element with the `data-onload-showmodal` attribute. This ensures the modal is displayed immediately after its content is loaded. ```javascript document.body.addEventListener("htmx:afterSettle", function(detail) { const dialog = detail.target.querySelector('dialog[data-onload-showmodal]'); if (dialog) { dialog.showModal(); }; }); ``` -------------------------------- ### Bundle HTMX with Custom JavaScript using django-compressor Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/base_template.rst Integrate HTMX with your project's JavaScript files using django-compressor. This ensures HTMX is loaded before your custom scripts, allowing you to use the HTMX JavaScript API. ```html+django {% compress js %} {% endcompress %} ``` -------------------------------- ### Custom Form Mixin with Renderer Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Create a form mixin that sets the custom Bulma form renderer and modifies default form behaviors, such as removing the default label suffix. ```python class BulmaFormMixin: default_renderer = BulmaFormRenderer() def __init__(self, *args, **kwargs) -> None: # We don’t want ':' as a label suffix: return super().__init__(*args, label_suffix="", **kwargs) ``` -------------------------------- ### Improved Paging with Inline Partials in Django HTMX Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This template demonstrates how to implement infinite scrolling or "load more" functionality using HTMX. It includes a loop for items and a "Load more" button that fetches and appends more content. ```html+django {# paging_with_inline_partials_improved_lob.html #} {% extends "base.html" %} {% block body %}

List of monsters

{% block page-and-paging-controls %} {% for monster in page_obj %}

{{ monster.name }}

{% endfor %} {% if page_obj.has_next %}

Load more

{% else %}

That's all of them!

{% endif %} {% endblock %} {% endblock %} ``` -------------------------------- ### Django Template: Add Monster Link Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/modals_main.html Placeholder for a link or button to add a new monster. Typically would link to a form or modal. ```html Add a monster {% endblock %} ``` -------------------------------- ### Single View for Paginated Content Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/separate_partials_single_view.rst A Django view that conditionally selects the template based on the HTMX request header to serve either the full page or a partial update. ```python def paging_with_separate_partials(request): if request.headers.get("Hx-Request", False): template_name = "_page_and_paging_controls.html" else: template_name = "paging_with_separate_partials.html" return TemplateResponse( request, template_name, { "page_obj": get_page_by_request(request, Monster.objects.all()), }, ) ``` -------------------------------- ### Define Form Renderer and Mixin Attributes Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Set template names as attributes on the form renderer and define a flag to control HTMX validation behavior. This helps in organizing logic and making HTMX validation opt-in. ```python class BulmaFormRenderer(TemplatesSetting): ... single_field_row_template = "forms/bulma/field_row.html" class BulmaFormMixin: ... do_htmx_validation = False def get_context(self, *args, **kwargs): return super().get_context(*args, **kwargs) | { "do_htmx_validation": self.do_htmx_validation, "single_field_row_template": self.renderer.single_field_row_template, } ``` -------------------------------- ### Django View for Modal Form Submission Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst Handles form submission within a modal. If the form is valid, it saves the data and returns an HttpResponse with custom 'Hx-Trigger' headers to close the modal and signal a successful creation. Otherwise, it renders the form in the modal. ```python form = CreateMonsterForm(request.POST) if form.is_valid(): monster = form.save() return HttpResponse( headers={ "Hx-Trigger": json.dumps( { "closeModal": True, "monsterCreated": monster.id, } ) } ) else: form = CreateMonsterForm() return TemplateResponse(request, "modals_create_monster.html", {"form": form}) ``` -------------------------------- ### Django View for HTMX Partial Update Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/separate_partials.rst This view handles the HTMX POST request. It updates the monster's state and returns only the updated partial template for the specific item. ```python @require_POST def toggle_item(request, monster_id): monster = Monster.objects.get(id=monster_id) monster.toggle_happiness() return TemplateResponse(request, "_toggle_item_partial.html", {"monster": monster}) ``` -------------------------------- ### Displaying Paginated List Items Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/_page_and_paging_controls.html Iterates over a Django page object to display a list of items. Use this to render the current page's content. ```html {% for monster in page_obj %} {{ monster.name }} {% endfor %} ``` -------------------------------- ### Django HTMX Form for Multiple Actions Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This HTML template defines a form that handles both 'kick' and 'hug' actions for a monster. It uses HTMX POST requests to submit the form and update the monster's state, targeting itself for an outerHTML swap. ```html+django {# multiple_actions.html (relevant block only) #} {% block monster-form %}
{% csrf_token %} {% if monster.is_happy %}

{{ monster.name }} is happy.

{% else %}

{{ monster.name }} is sad.

{% endif %}
{% endblock %} ``` -------------------------------- ### HTML Form with CSRF Token for POST Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/posts.rst Use this approach when submitting form data. Ensure the `{% csrf_token %}` tag is included within the form for security. ```html+django

Make some monsters!

{% csrf_token %} How many:
``` -------------------------------- ### Conditional Rendering with Django Templates Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/_toggle_item_partial.html Use Django's `{% if %}` and `{% else %}` tags to conditionally display content based on a model's attribute. This is useful for showing different UI elements or messages. ```html {{ monster.name }} is {% if monster.is_happy %}happy{% else %}sad{% endif %} {% if monster.is_happy %}Kick it!{% else %}Hug it!{% endif %} ``` -------------------------------- ### Use `for_htmx` decorator for partial template responses (separate template) Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt When a view is called via htmx, this decorator intercepts the `TemplateResponse` and returns only a specified partial template instead of the full page. This option uses a separate HTML file for the partial response. ```python # views/partials.py from django.template.response import TemplateResponse from htmx_patterns.utils import for_htmx from htmx_patterns.models import Monster from django.core.paginator import Paginator def get_page_by_request(request, queryset, paginate_by=6): return Paginator(queryset, per_page=paginate_by).get_page( request.GET.get("page") ) # Option 1: switch to a separate partial template on htmx requests @for_htmx(use_template="_page_and_paging_controls.html") def paging_with_separate_partials(request): return TemplateResponse( request, "paging_with_separate_partials.html", {"page_obj": get_page_by_request(request, Monster.objects.all())}, ) ``` -------------------------------- ### Django View with Multiple POST Actions (No HTMX) Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/actions.rst This Python view handles different POST requests based on form input names. It redirects to the same URL after processing actions. Use when not using HTMX or for non-HTMX fallbacks. ```python def monster_detail(request: HttpRequest, monster_id: int): monster: Monster = get_object_or_404(Monster.objects.all(), id=monster_id) if request.method == "POST": if "kick" in request.POST: monster.kick() elif "hug" in request.POST: monster.hug() return HttpResponseRedirect("") return TemplateResponse( request, "monster_detail.html", { "monster": monster, }, ) ``` -------------------------------- ### Django Template for Paginated Monster List Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/code/htmx_patterns/templates/paging_with_inline_partials.html This template displays a list of monsters with pagination controls. It checks if the monster list is empty and conditionally renders the pagination. The 'page-and-paging-controls' block is designed to be used with HTMX for partial updates. ```html {% extends "base.html" %} {% block body %} List of monsters ================ {% if page_obj.paginator.count == 0 %} We have no monsters! {% else %} {% block page-and-paging-controls %} {% for monster in page_obj %} {{ monster.name }} {% endfor %} {% if page_obj.has_next %} [Load more](#) {% else %} That's all of them! {% endif %} {% endblock %} {% endif %} See also: [create monsters page]({% url 'simple_post_form' %}) {% endblock %} ``` -------------------------------- ### Toggle Individual Items with Separate Partial Templates in Django Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This pattern uses two Django views: one for the main page and another for a partial template that updates individual items. The partial view is called via HTMX POST requests. ```python # views/partials.py from django.template.response import TemplateResponse from django.views.decorators.http import require_POST from htmx_patterns.models import Monster def toggle_with_separate_partials(request): return TemplateResponse( request, "toggle_with_separate_partials.html", {"monsters": Monster.objects.all()}, ) @require_POST def toggle_item(request, monster_id): monster = Monster.objects.get(id=monster_id) monster.is_happy = not monster.is_happy monster.save() # Returns only the partial for the updated monster card return TemplateResponse( request, "_toggle_item_partial.html", {"monster": monster}, ) ``` -------------------------------- ### Main Template for Paginated List Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/separate_partials_single_view.rst The main Django template includes a conditional check for content and renders the pagination controls. ```html+django {% extends "base.html" %} {% block body %}

List of monsters

{% if page_obj.paginator.count == 0 %}

We have no monsters!

{% else %} {% include "_page_and_paging_controls.html" %} {% endif %} {% endblock %} ``` -------------------------------- ### HTML Structure for a Modal Dialog with Form Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/modals.rst This HTML defines a modal dialog containing a form. The form uses HTMX to POST data back to the same view, targeting the dialog itself for updates. It includes a submit button and renders form fields. ```html {% block dialog-contents %}
{{ form.as_p }}
{% endblock %}
``` -------------------------------- ### Modal Template for Creating a Monster Source: https://context7.com/spookylukey/django-htmx-patterns/llms.txt This Django template defines the structure of the modal dialog. It uses HTMX to submit the form and targets itself for updates, with a close button. ```html+django {# modals_create_monster.html #} {% block dialog-contents %}
{{ form.as_p }}
{% endblock %}
``` -------------------------------- ### SCSS for Bulma Styled Inputs Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst This SCSS code uses the @extend rule to apply Bulma's input and checkbox styles to standard HTML input elements within a .field-body container. It simplifies styling by avoiding repetitive class application in templates. ```scss @import "../vendor/bulma.scss"; .field-body { input[type=text], input[type=email], input[type=password], input[type=date] { @extend .input; } input[type=checkbox] { @extend .checkbox; } } ``` -------------------------------- ### Bulma Form Template Source: https://github.com/spookylukey/django-htmx-patterns/blob/master/form_validation.rst Customize the Django form rendering template to output HTML compatible with Bulma's form structure, including handling labels and errors. ```html+django {% load widget_tweaks %} {{ errors }} {% if errors and not fields %}
{% for field in hidden_fields %}{{ field }}{% endfor %}
{% endif %} {% for field, errors in fields %} {% if field.label %}
{{ field.label_tag }}
{% endif %}
{% with error_class=errors|yesno:"is-danger,, " %}
{% if field|widget_type == "select" %} ```