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 handler
future-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