Following pattern uses Partial Page Load, it uses Bootsrap modals and is compatible with No-JS browser environments. it follows HTMX - Partial page load pattern to load edit form inside a modal.
Django Code for view.py
for model called Corpora
and a model form called CorpusForm
:
from datacore.models import Corpora, Document
from datacore.forms import CorpusForm
corpus = Corpora.objects.get(id=id)
# Handle edit request
if (request.method == 'POST' and request.POST.get('edit')=='1') or (request.htmx and request.htmx.trigger_name=='edit'):
form = CorpusForm(instance=corpus)
context = {
'corpus': corpus,
'form': form,
}
context['base_template'] = "partial.html" if request.htmx else "base.html"
return render(request, "corpus-edit.html", context)
if request.method == 'POST' and request.POST.get('save')=='1':
form = CorpusForm(request.POST or None, instance=corpus)
corpus = form.save()
# Handle delete request
if request.method == "DELETE" or (request.method == 'POST' and request.POST.get('delete')=='1'):
corpus.delete()
if request.htmx:
return corpora(request)
else:
return HttpResponseRedirect(reverse('datacore:corpora'))
context = {
'corpus': corpus,
}
context['base_template'] = "partial.html" if request.htmx else "base.html"
return render(request, "corpus.html", context)
corpus-edit.html
file called from view.
{% extends base_template %}
{% load i18n %}
{% load widget_tweaks %}
{% block title %}{% trans "Corpora" %}{% endblock %}
{% block content %}
<form action="{% url 'datacore:corpus' corpus.id %}" method="post">
{% csrf_token %}
<div class="modal-header">
<h1 class="modal-title fs-5" id="modal-label">Edit Corpus</h1>
{% if base_template != 'base.html' %}<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>{% endif %}
</div>
<div class="modal-body">
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label">{{ field.label }}</label>
{% if field|field_type == 'charfield' and field|widget_type == 'textarea' %}
{# render textarea #}
{% render_field field class="form-control rounded-0" %}
{% elif field|field_type == 'modelmultiplechoicefield' and field|widget_type == 'checkboxselectmultiple' %}
{# render checkbox #}
{% for choice in field %}
<div class="form-check">
<input class="form-check-input" type="checkbox" value="{{ choice.data.value }}" id="{{ choice.id_for_label }}" name="{{ choice.data.name }}"{% if choice.data.selected %} checked{% endif %}>
<label for="{{ choice.id_for_label }}" class="form-check-label">
{{ choice.choice_label }}
</label>
</div>
{% endfor %}
{% else %}
{# default render for most fields #}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<div id="{{ field.id_for_label }}-help" class="form-text">{{ field.help_text|safe }}</div>
{% endif %}
</div>
{% endfor %}
</div>
<div class="modal-footer gap-2">
<a href="{% url 'datacore:corpus' corpus.id %}" class="btn btn-secondary" {% if base_template != 'base.html' %} data-bs-dismiss="modal"{% endif %}>Back</a>
<button type="submit" class="btn btn-primary" name="save" value="1">Save</button>
</div>
</form>
{% endblock %}
corpora.html which shows buttons which perform deleting action, opening edit form(in modal, or in case of No-JS in new page):
<div id="loading-indicator" class="htmx-indicator spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<form method="post" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
{% csrf_token %}
<div class="btn-group float-end" role="group" aria-label="Basic example">
<button hx-indicator="#loading-indicator" hx-post="{% url 'datacore:corpus' corpus.id %}" hx-target="#modal-container" type="submit" name="edit" value="1" class="btn btn-outline-info" data-bs-toggle="modal" data-bs-target="#page-modal"><i class="fa fa-pen"></i></button>
<button hx-indicator="#loading-indicator" hx-delete="{% url 'datacore:corpus' corpus.id %}" hx-target="#main-container" hx-confirm="Are you sure you?" type="submit" name="delete" value="1" class="btn btn-outline-danger"><i class="fa fa-trash-can"></i></button>
</div>
</form>