Here is the Django approach to the Bulk Update example from the HTMX Website. You can get the full working example from here .
$ python manage.py startapp bulk_update
This creates a django app in the root but I then move it into the django_htmx_examples as that is my preference.
We need to create a model and as part of our migration I want to add data to make it easy for people to see it working straight away when they pull the example down from github.
# django_htmx_examples/bulk_update/models.py
from django.db import models
# Create your models here.
class Customer(models.Model):
name = models.CharField(null=False, blank=False, max_length=50)
email = models.EmailField(null=False, blank=False, max_length=254)
status = models.BooleanField(null=False, blank=False)
def __str__(self):
return f"{self.name}"
I've given it the name Customer. Now I want to makemigrations and then tweak the file to populate the table after creation with some data.
python manage.py makemigrations
# django_htmx_examples/bulk_update/migrations/0001_initial.py
# Generated by Django 3.2.6 on 2021-09-09 20:09
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
def create_customer(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Customer = apps.get_model("bulk_update", "Customer")
Customer.objects.update_or_create(
email="dr@htmx.org", defaults={"name": "Django", "status": True}
)
Customer.objects.update_or_create(
email="brian@htmx.org", defaults={"name": "Brian", "status": True}
)
Customer.objects.update_or_create(
email="celia@htmx.org", defaults={"name": "Celia", "status": True}
)
Customer.objects.update_or_create(
email="emma@htmx.org", defaults={"name": "Emma", "status": True}
)
operations = [
migrations.CreateModel(
name="Customer",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=50)),
("email", models.EmailField(max_length=254)),
("status", models.BooleanField()),
],
),
migrations.RunPython(create_customer),
]
I use a migrations.RunPython call in my migrations file to populate the table using the create_customer function following guidance from the django docs
# django_htmx_examples/urls.py
# This is the relevant section
path(
"bulk-update/",
bulk_update_views.customer_status,
name="bulk-update-customer-status",
),
path(
"bulk-update/activate",
bulk_update_views.customer_activate,
name="bulk-update-customer-activate",
),
path(
"bulk-update/deactivate",
bulk_update_views.customer_deactivate,
name="bulk-update-customer-deactivate",
),
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.urls import reverse
from django.views.decorators.http import require_http_methods
from rest_framework.decorators import api_view
from rest_framework.response import Response
import json
from rest_framework.parsers import JSONParser
from django.http import QueryDict
from .models import Customer
from .forms import Customer
def customer_status(request):
context = {}
customers = Customer.objects.all()
context["customers"] = customers
return render(request, "bulk_update/customer_status.html", context)
@require_http_methods(["PUT"])
def customer_activate(request):
context = {}
data = request.body.decode("utf-8")
q = QueryDict(data, mutable=True)
activated_ids = q.getlist("ids")
Customer.objects.filter(pk__in=activated_ids).update(status=True)
customers = Customer.objects.all()
context["customers"] = customers
return render(request, "bulk_update/customer_status_update.html", context)
@require_http_methods(["PUT"])
def customer_deactivate(request):
context = {}
data = request.body.decode("utf-8")
q = QueryDict(data, mutable=True)
activated_ids = q.getlist("ids")
Customer.objects.filter(pk__in=activated_ids).update(status=False)
customers = Customer.objects.all()
context["customers"] = customers
return render(request, "bulk_update/customer_status_update.html", context)
I have used the QueryDict here to pull out the duplicate ids in the PUT request. This seems to work so I am going with it! If there is a better way do let me know on twitter @chriswedgwood
# django_htmx_examples/templates/bulk_update/customer_status.html
{% extends "base.html" %}
{% block content %}
<form id="checked-contacts">
<table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody id="tbody">
{% for customer in customers %}
<tr class="">
<td><input type='checkbox' name='ids' value='{{customer.id}}'></td>
<td>{{customer.name}}</td>
<td>{{customer.email}}</td>
<td>{% if customer.status %} Active {% else %} Inactive {% endif %} </td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
<div hx-include="#checked-contacts" hx-target="#tbody">
<button hx-put="activate" type="button" class="ml-2 inline-flex mt-10 items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Activate
</button>
<button hx-put="deactivate" type="button" class="ml-2 inline-flex mt-10 items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Deactivate
</button>
</div>
{% endblock content %}
# django_htmx_examples/templates/bulk_update/customer_status_update.html
{% for customer in customers %}
<tr class="">
<td><input type='checkbox' name='ids' value='{{customer.id}}'></td>
<td>{{customer.name}}</td>
<td>{{customer.email}}</td>
<td>{% if customer.status %} Active {% else %} Inactive {% endif %} </td>
</tr>
{% endfor %}