great-cartoon-12331
05/12/2023, 5:55 PMlogin_required decorator that checks whether the request is from htmx, and if so generates a response that will trigger htmx to do a client-side redirect. htmx doesn't do this because it's a server-side concernhandsome-gpu-11538
05/12/2023, 5:57 PMgreat-cartoon-12331
05/12/2023, 6:29 PMXMLHTTPRequest in JavaScript so it's at a layer below htmx even sees.handsome-gpu-11538
05/12/2023, 6:39 PMgreat-cartoon-12331
05/12/2023, 6:56 PM@htmx decorator that would sit on top of @login_required and check if the request is from htmx and if the response is a 3xx redirect, and if so trigger a client-side redirect.proud-librarian-99598
05/12/2023, 6:57 PMgreat-cartoon-12331
05/12/2023, 7:05 PMpython
def htmx(f):
'''
E.g.
@htmx
@login_required
def ...
'''
def handler(req, *args, **kwargs):
resp = f(req, *args, **kwargs)
if "HX-Request" in request.headers and resp.status_code in {301, 302, 303}:
response.headers['HX-Redirect'] = response.headers['Location']
del response.headers['Location']
response.status_code = 200
return resp
return handlerfuture-table-82610
05/13/2023, 6:21 AMhtmx returns a response parts of the context seem ot be "skipped". I have some custom middleware that inserts a variable - doesn't happen. Then I started noticing on a few`, request` and user. It's super weird and I can't find ay reason for it. Has anyone else ever seen anything like that?jolly-breakfast-57126
05/13/2023, 11:19 AMhandsome-shampoo-48908
05/15/2023, 5:13 PMpy
def index(request):
context = {}
collection_slug = request.GET.get("collection")
form_collection = CollectionForm()
form_task = TasksForm()
if not collection_slug:
Collection.get_default_collection()
return redirect(f"{reverse('home')}?collection=_default")
collection = get_object_or_404(Collection, slug=collection_slug)
context["collections"] = Collection.objects.order_by("slug")
context["collection"] = collection
context["tasks"] = collection.task_set.order_by("description")
context["form_collection"] = form_collection
context["form_task"] = form_task
return render(request, 'tasks/index.html', context=context)
def add_collection(request):
collection_name = escape(request.POST.get("name"))
collection, created = Collection.objects.get_or_create(name=collection_name, slug=slugify(collection_name))
if not created:
return HttpResponse("The collection already exists.", status=409)
response = HttpResponse()
response["HX-Redirect"] = f"{reverse('home')}?collection={slugify(collection_name)}"
return response
https://cdn.discordapp.com/attachments/864934037381971988/1107717283146637412/image.pngβΎ
handsome-shampoo-48908
05/15/2023, 5:13 PMhtml
<form method="POST" class="d-flex gap-2 mt-2">
{% csrf_token %}
{{ form_collection.name }}
<button class="btn btn-success"
hx-post="{% url 'add-collection' %}"
hx-target="#collections"
hx-swap="beforeend"
type="submit">Add
</button>
</form>
Tell me if you want more info.
Thanks in advance for your reply and your help.bitter-machine-55943
05/15/2023, 11:10 PMdjango-components compared to React/Vue components? Is the idea to make small components and piece them together to make more complex components (like in React/Vue/etc)?ripe-action-67367
05/16/2023, 7:42 AMrender(request, 'tasks/index.html', context=context)
from add_collection method. Set context["collection"] to your newly created collectionhandsome-shampoo-48908
05/16/2023, 8:33 AMripe-action-67367
05/16/2023, 8:34 AMresponse["HX-Push"] = f"{reverse('home')}?collection={slugify(collection_name)}"ripe-action-67367
05/16/2023, 8:45 AMHX-Push-Url https://htmx.org/headers/hx-push-url/handsome-shampoo-48908
05/16/2023, 6:16 PMripe-action-67367
05/16/2023, 6:55 PMindex method. Make sure your form uses the same target and swaphandsome-shampoo-48908
05/17/2023, 9:22 AMripe-action-67367
05/17/2023, 9:27 AMcontext["collection"] = collection
context["tasks"] = collection.task_set.order_by("description")
I assume the "collection" value represents current collection. Make sure to set it to newly created one. Same for "tasks". If you do that but it doesn't work, there must be something I'm missing with how your template is renderedhandsome-shampoo-48908
05/17/2023, 9:44 AMpy
def index(request):
context = {}
collection_slug = request.GET.get("collection")
form_collection = CollectionForm()
form_task = TasksForm()
if not collection_slug:
Collection.get_default_collection()
return redirect(f"{reverse('home')}?collection=_default")
collection = get_object_or_404(Collection, slug=collection_slug)
context["collections"] = Collection.objects.order_by("slug")
context["collection"] = collection
context["tasks"] = collection.task_set.order_by("description")
context["form_collection"] = form_collection
context["form_task"] = form_task
return render(request, 'tasks/index.html', context=context)
def add_collection(request):
collection_name = escape(request.POST.get("name"))
collection, created = Collection.objects.get_or_create(name=collection_name, slug=slugify(collection_name))
if not created:
return HttpResponse("The collection already exists.", status=409)
response = HttpResponse()
response["HX-Redirect"] = f"{reverse('home')}?collection={slugify(collection_name)}"
return response
def add_task(request):
collection = Collection.objects.get(slug=request.POST.get("collection"))
description = escape(request.POST.get("description"))
task = Task.objects.create(description=description, collection=collection)
return render(request, 'tasks/task.html', context={"task": task})handsome-shampoo-48908
05/17/2023, 9:44 AMpy
def delete_task(request, task_pk):
task = get_object_or_404(Task, pk=task_pk)
task.delete()
return HttpResponse("")
def delete_collection(request, collection_pk):
collection = get_object_or_404(Collection, pk=collection_pk)
collection.delete()
response = HttpResponse()
response["HX-Redirect"] = f"{reverse('home')}?collection=_default"
return response
def get_tasks(request, collection_pk):
collection = get_object_or_404(Collection, pk=collection_pk)
tasks = collection.task_set.order_by("description")
return render(request, 'tasks/tasks.html', context={"tasks": tasks, "collection": collection})handsome-shampoo-48908
05/17/2023, 9:45 AMhtml
<div class="container mx-auto mt-5">
<header class="d-flex align-items-center gap-2 mb-5">
<img src="https://cdn-icons-png.flaticon.com/128/2666/2666436.png" alt="ToDo List">
<h1 class="mb-0">To Do List</h1>
</header>
<hr>
<br>
<div class="row justify-content-around">
<div class="card col-sm-6 col-md-4 col-lg-4 mb-5">
<h5 class="card-header">Collections</h5>
<div class="card-body">
<nav id="collections" class="list-group">
{% for collection in collections %}
{% include 'tasks/collection.html' with collection=collection %}
{% endfor %}
</nav>
<form method="POST" class="d-flex gap-2 mt-2">
{% csrf_token %}
{{ form_collection.name }}
<button class="btn btn-success"
hx-post="{% url 'add-collection' %}"
hx-target="#collections"
hx-swap="beforeend"
type="submit">Add
</button>
</form>
</div>
</div>handsome-shampoo-48908
05/17/2023, 9:45 AMhtml
<div class="card col-sm-6 col-md-6 col-lg-6 mb-5">
<h5 class="card-header">Tasks</h5>
<div class="card-body">
<form method="POST" name="task-form" class="d-flex gap-2 mt-2" id="task_form">
{% csrf_token %}
{{ form_task.description }}
<button class="btn btn-success"
hx-post="{% url 'add-task' %}"
hx-target="#tasks"
hx-vals='js:{collection: getCollectionFromURL()}'
hx-swap="beforeend"
type="submit"
>Add
</button>
</form>
<section id="tasks-container">
{% include 'tasks/tasks.html' with tasks=tasks collection=collection %}
</section>
</div>
</div>
</div>
</div>handsome-shampoo-48908
05/17/2023, 9:45 AMhtml
<script>
document.body.addEventListener("htmx:afterRequest", (event) => {
document.querySelector(".reset-field-collection-form").value = '';
})
document.body.addEventListener("htmx:afterRequest", (event) => {
document.querySelector(".reset-field-task-form").value = '';
})
document.body.addEventListener("htmx:responseError", (event) => {
alert(evt.detail.xhr.responseText);
})
function getCollectionFromURL() {
let url = new URL(window.location.href);
let searchParams = url.searchParams;
return searchParams.get("collection");
}
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>mammoth-family-48524
05/17/2023, 10:08 AMwhite-motorcycle-95262
05/17/2023, 2:14 PMhx- attributes that listen for a particular change event (all coming from a single form, usually just has two fields: a select for the "region" and a toggle for the "timespan", short or long term). All widgets are tied to a class based "WidgetView" that has methods for generating the HTML associated with each type of widget.
The idea is that to create a dashboard, all I have to do is create a base template with columns/rows and put {% include 'widgets/widget_name.html' %} where I want my widgets to go. This works, but it results in a single request for each widget, which I'm not opposed to, but in reality the widgets are quite coupled together: they always change all at the same time, and there's a small amount of "setup" code+database hits that end up being duplicated each request.
I suppose the alternate way would be to put the hx- attributes on the form and do OOB swaps, which would result in a single request. My question is: what can I do on the form to communicate to the server which widgets are present on the dashboard? The only thing I've thought up involves hyperscript:
<form
id="control-panel
hx-get="/widgets"
_="
init
set widgets to {}
for widget in <[data-widget]/>
set widgets[widget's @id] to widget's @data-widget
end
set my @hx-vals to widgets as JSON
end
>[...]</form>
where each widget would have a data-widget attribute that specifies the widget's "type" (e.g., map, graph, table, etc).
Any thoughts or criticisms on this approach would be appreciated πbrave-dog-98297
05/17/2023, 9:27 PMwhite-motorcycle-95262
05/17/2023, 11:30 PMbrave-dog-98297
05/17/2023, 11:38 PM