Dynamically update table when creating new enty using HTMX - django

After recent success in some simple HTMX tasks I wanted to extend adamchainz django-htmx example by a modal field that updates a table dynamically. I am not sure if I am returning the right thing in render ... but my problem is, the table is not being updated. Only when I hit reload.
view:
class CreateProductView(TemplateView):
template_name = "app/product_create.html"
form_class = OrderForm
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {'form': self.form_class()})
def post(self, request):
Product.objects.create(name = request.POST["name"], price = request.POST["price"])
part_template = "app/partial-rendering.html"
return render(request, part_template, {"base_template": "app/_partial.html"})
urls.py:
path("create/", views.CreateProductView.as_view(), name = 'create'),
This is my index.html with the table in it:
<div class="..."><button class="btn btn-primary"
id="showButton"
hx-get="/create"
hx-target="#modal-create">create</button</div>
<main role="main" id="main">{% include "app/orders_table.html" %}</main>
<div id="modal-create"></div>
I also have a partial-rendering.html in place:
{% extends base_template %}
{% block main %}
{% include "app/product_table.html" %}
{% endblock %}
and a _partial.html:
<main id="main">
{% block main %}{% endblock %}
</main>
I will not post the whole product_table.html here, I guess it is straight forward ... mainly:
<table class="table table-striped table-hover">
<thead>
<tr>
<th>product name</th>
<th>price</th>
</tr>
</thead>
<tbody>
{% for product in page.object_list %}
<tr>
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The data from the form is collected via JS and sent to django using AJAX. I did not use the normal form submit because I wanted to avoid a page reload (which would solve that problem, I know).
I did try to put this together from the example mentioned above. Everything except the dynamic page update runs well!

Some approach is to remove the div targeted:
<div id="modal-create"></div>
then in your table body write the id deleted:
<tbody id="modal-create">
you want some partial for your table loops
{% for product in page.object_list %}
{% include 'some_table_body_tr_partial.html' %}
{% endfor %}
your partial might contain something like this:
<tr hx-target="this" hx-swap="outerHTML">
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
This approach have an issue: the width of the form will take the wide of the first column.

Related

How to build a table of objects without knowing their attributes?

I'm trying to build a table dynamically in my django template. I got the column names in my views.py and also got the contents of the table:
table = tenant_tables.models[table_name].objects.all()
headers = table.model._meta.get_fields(include_parents=True, include_hidden=False)
context = {'table': table, 'headers': headers}
return render(request, template_name=template_name, context=context)
Based on the received data, I want to build a table using a template, but I don't know how to sort through all the attributes of the object
<table
id="example"
class="table table-striped data-table"
style="width: 100%"
>
<thead>
<tr>
{% for header in headers %}
<th>{{header.verbose_name}}</th>>
{% endfor %}
</tr>
</thead>
<tbody>
{% for obj in table %}
<tr>
<td>{{obj}}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
{% for header in headers %}
<th>{{header.verbose_name}}</th>>
{% endfor %}
</tr>
</tfoot>
</table>
Can I implement this or should I create a view for each model in my application?
Yes, it's possible to implement this. But first you need to implement a custom filter which works like the getattr function in python.
Then you can do this:
...
<tbody>
{% for obj in table %}
<tr>
{% for header in headers %}
<td>{{ obj | getattr_filter:header }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
...
The filter should looks something like this:
#register.filter(name='getattr_filter')
def getattr_filter(obj, attribute):
return getattr(obj, attribute, None)
Check the docs for more details on how to implement a custom template filter.

How do I bulk_delete in django?

I need to delete select items by a checkbox and delete all the so selected items.
I tried this but it doesn't work.
I have tried other similar questions but couldn't get help
<table class="layout container">
<thead>
<th>Name</th>
<th>Mark</th>
</thead>
{% for book in books %}
<tr>
<td>{{ book.book_name }}</td>
<td>
<input type="checkbox" name="marked_delete" value="{{ book.pk }}" {% if '{{ book.pk }}' in 'marked_delete' %} checked {% endif %}>
</td>
</tr>
{% endfor %}
</table>
The view
def delete_bulk_books(request):
book = request.GET.get('marked_delete')
books = Books.objects.filter(id__in=marked_delete).delete()
return redirect("view_books")
You can use getlist(…) [Django-doc], instead of .get('marked_delete'):
def delete_bulk_books(request):
book = request.GET.getlist('marked_delete')
books = Books.objects.filter(id__in=marked_delete).delete()
return redirect('view_books')
Normally however requests that have delete are done through a POST or DELETE request, you thus might want to work with a form that makes a POST request.

Get ID of a ManyToManyField on ModelForm

I have a particular question envolving the editing of a model with a m2m field that i can't seem to find an answer anywhere. So i have a model, Equipment with an m2m relation to Tests.
models.py:
class Equipment(models.Model):
...
tests = models.ManyToManyField(Test, blank=True)
def __str__(self):
return '%s %s' % (self.manufacturer, self.model)
pass
forms.py:
class EquipmentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
modality = kwargs.pop('modality', None)
super (EquipmentForm, self).__init__(*args, **kwargs)
self.fields['tests'].widget = forms.CheckboxSelectMultiple()
self.fields['tests'].queryset = Test.objects.filter(modality=modality).order_by('group', 'number')
class Meta:
model = Equipment
fields = ['tests']
html:
...
<tbody>
{% for f in form.tests %}
<tr>
<td>{{ f }}</td>
<td>
<a data-toggle="modal" href="{% url 'app:modal' equipament_id=equipament_id test_id=f.id %}" data-target="#tests">See</a>
</td>
</tr>
{% endfor %}
</tbody>
As you can see i am trying to open a modal for each test field in the form so the user can see some information related to that test. This is where i am having trouble because i cant seem to get that f.id to pass it to a view that renders the modal dialog. Any ideas of how to do that? Please help.
First Attempt
{% for field in form.visible_fields %}
{% if field.name == "tests" %}
<table>
{% for choice in field.queryset %}
<tr>
<td><input value="{{choice.id}}" type="radio" name="tests" />{{choice.description}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endfor %}
This doesn't work because of the queryset. From the documentation it's not supported for a ModelMultipleChoiceField.
Second Attempt
<table>
<tbody>
{% for pk, choice in form.tests.field.choices %}
<tr>
<td>{{ choice }}</td>
<td><a data-toggle="modal" href="{% url 'SFM:modal' equipment_id=equipment_id test_id=pk %}" data-target="#tests">Ver</a></td>
</tr>
{% endfor %}
</tbody>
</table>
With this approach i am able to pass pk to the view and load the corresponding modal but i lost my form checkbox. If i manually render it i am not able to save any checkbox. This tells me that it is possible to pass the pk to view but maybe it should be acessible by querying the field form and not only in the choice. I am running out of ideas.

Displaying a Table in Django from Database

How do you display the information from a database table in a table format on a webpage? Is there a simple way to do this in django or does it require a more complicated approach. More specifically, how do you pretty much port over the columns and rows in a database table to a visual table that can be seen from a url?
The easiest way is to use a for loop template tag.
Given the view:
def MyView(request):
...
query_results = YourModel.objects.all()
...
#return a response to your template and add query_results to the context
You can add a snippet like this your template...
<table>
<tr>
<th>Field 1</th>
...
<th>Field N</th>
</tr>
{% for item in query_results %}
<tr>
<td>{{ item.field1 }}</td>
...
<td>{{ item.fieldN }}</td>
</tr>
{% endfor %}
</table>
This is all covered in Part 3 of the Django tutorial. And here's Part 1 if you need to start there.
$ pip install django-tables2
settings.py
INSTALLED_APPS , 'django_tables2'
TEMPLATES.OPTIONS.context-processors , 'django.template.context_processors.request'
models.py
class hotel(models.Model):
name = models.CharField(max_length=20)
views.py
from django.shortcuts import render
def people(request):
istekler = hotel.objects.all()
return render(request, 'list.html', locals())
list.html
{# yonetim/templates/list.html #}
{% load render_table from django_tables2 %}
{% load static %}
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="{% static
'ticket/static/css/screen.css' %}" />
</head>
<body>
{% render_table istekler %}
</body>
</html>
If you want to table do following steps:-
views.py:
def view_info(request):
objs=Model_name.objects.all()
............
return render(request,'template_name',{'objs':obj})
.html page
{% for item in objs %}
<tr>
<td>{{ item.field1 }}</td>
<td>{{ item.field2 }}</td>
<td>{{ item.field3 }}</td>
<td>{{ item.field4 }}</td>
</tr>
{% endfor %}
The answers in this thread rely on manually feeding column names, and I prefer to have some way of viewing a Django model completely by default. I have cooked up the solution below:
views.py
from django.shortcuts import render
from .models import TABLEOFINTEREST
def TABLEOFINTEREST(request):
MODEL_HEADERS=[f.name for f in TABLEOFINTEREST._meta.get_fields()]
query_results = [list(i.values()) for i in list(TABLEOFINTEREST.objects.all().values())]
#return a response to your template and add query_results to the context
return render(request, "/TABLEOFINTEREST.html", {
"query_results" : query_results,
"model_headers" : MODEL_HEADERS
})
TABLEOFINTEREST.html:
<table>
<tr>
{% for item in model_headers %}
<th>{{ item }}</th>
{% endfor %}
</tr>
{% for all_rows in query_results %}
<tr>
{% for every_column in all_rows %}
<td>{{ every_column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("TABLEOFINTEREST", views.TABLEOFINTEREST, name="TABLEOFINTEREST")
]
Tested and validated on my machine with multiple tables.

Why doesnt the If statement in django work?

I was working from an example of an 'if' statement, but for some reason I cannot get it to work.
I tried every possible statement in the 'if', but it seems as though nothing makes it true for the if statement to run. Instead the Else statement runs.
Sample
{% extends 'base.html' %}
{% block content %}
<h2>Hardware Inventory</h2>
{% if hardware %}
<table id="datatable">
<thead>
<tr>
<th>Name</th>
<th>Manufacturer</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{% for item in hardware %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.manufacturer }}</td>
<td>{{ item.kind }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>The inventory is empty.</p>
{% endif %}
{% endblock %}
Mine
{% extends 'base.html' %}
{% block content %}
<h2>News</h2>
{% if entry %}
{% for item in entry %}
<table id = "news">
<tr>
<td>{{ item.title }}</td>
<td>{{ item.body }}</td>
<td>{{ item.pub_date }}</td>
</tr>
</table>
{% endfor %}
{% else %}
<p>No News</p>
{% endif %}
{% endblock %}
My view.py for news, Im not sure how to write it correctly, but i tried different combinations, this one errors at the moment causes it to crash
def index(request):
return render_to_response('news/index.html', {'Entry': Entry}, context_instance=RequestContext(request))
def Entry(request):
Entry = Entry.objects.all().order_by('pub_date')
return render_to_response('news/Entry.html', {'item':item}, context_instance=RequestContext(request))
Make sure you're actually passing entry into the context. For example:
render_to_response('template.html', { 'entry': entry })
Unset variables behave as variables set to None in Django templates.
UPDATE:
Made some revisions to your view code; not even sure how you got to the template rendering with what you had.
Original:
def index(request):
return render_to_response('news/index.html', {'Entry': Entry}, context_instance=RequestContext(request))
def Entry(request):
Entry = Entry.objects.all().order_by('pub_date')
return render_to_response('news/Entry.html', {'item':item}, context_instance=RequestContext(request))
Modified:
def index(request):
entry = Entry.objects.all().order_by('pub_date')
return render_to_response('news/index.html', {'entry': entry}, context_instance=RequestContext(request))
I don't think you even needed the Entry method, so removed that. I kept your naming the same, but it's better form to call that variable entries since it's multiple items.