Python list of dict to html table (Flask) - python

I have a list of dict:
data = [{'2479': {'2022-09-04 to 2022-09-10': 28, '2022-08-28 to 2022-09-03': 27},
'ADMINISTRATION': {'2022-09-04 to 2022-09-10': 8},
'1361': {'2022-09-04 to 2022-09-10': 4, '2022-08-28 to 2022-09-03': 5},
'PERSONAL TIME OFF': {'2022-08-28 to 2022-09-03': 8}}]
# '2479' is the project number
# '2022-09-04 to 2022-09-10' is the date range
# 28 is the total number of hours used for the project for that week
I'm trying to convert it to an html table like this:
So far, this is what I have:
Here's my code:
{% macro render_table_header(label) %}
{% for record in content["weeklyUserReport"] %}
{% for project in record %}
{% for week in record[project] %}
<th class="rotate-45 week1">
<div><span class="date">{{week}}</span></div>
</th>
{% endfor %}
{% endfor %}
{% endfor %}
{% endmacro %}
<table
class="table table-header-rotated table-striped-column hours-container"
id="grid"
>
<thead>
<tr>
<th class="row-header"></th>
{{ render_table_header() }}
</tr>
</thead>
<tbody>
{% for record in content["weeklyUserReport"] %}
{% for project in record %}
{% for week in record[project] %}
<tr>
{% if record[project] == project %}
<td class="project-name"></td>
{% else %}
<td class="project-name">{{ project }}</td>
{% endif %}
<td class="week1">{{ record[project][week] }}</td>
</tr>
{% endfor %}
{% endfor %}
{% endfor %}
</tbody>
</table>
I'd like duplicated projects to be removed and for the hours to line in to their corresponding rows. I have <td class="week2">, <td class="week3">, and <td class="week4"> for the other hours but I'm struggling to find a way to do that.
Any suggestions would be greatly appreciated! Thank you.

I think you were confused about how to properly do loops through the list of dictionaries on the jinja template. I can give a "replica" of what you have done. Try to do something like this on your HTML:
<body>
<table class="table table-header-rotated table-striped-column hours-container" id="grid" style="border: 1px solid black">
<thead>
<tr>
<th style="border: 1px solid black">project name</th>
{% for d in date %}
<th class="row-header" style="border: 1px solid black">{{d}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for record in data %}
{% for project_key, project_value in record.items() %}
<tr>
<td>{{ project_key }}</td>
{% for value in project_value.values() %}
<td class="week1" style="border: 1px solid black">{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</body>
Here is my controller:
#app.route("/")
def index():
data = [{'2479': {'2022-09-04 to 2022-09-10': 28, '2022-08-28 to 2022-09-03': 27},
'ADMINISTRATION': {'2022-09-04 to 2022-09-10': 8},
'1361': {'2022-09-04 to 2022-09-10': 4, '2022-08-28 to 2022-09-03': 5},
'PERSONAL TIME OFF': {'2022-08-28 to 2022-09-03': 8
}}]
date = ['2022-09-04', '2022-08-28']
return render_template('index.html', date=date, data=data)
Here is the result I got:

Related

Load CSV file in Django view and turn into HTML table

I have this view code
def datatable(request, file):
csv_fp = open(f'data/{file}.csv', 'r')
reader = csv.DictReader(csv_fp)
headers = [col for col in reader.fieldnames]
out = [row for row in reader]
return render(request, 'datatable.html', {'data' : out, 'headers' : headers})
and here's my template
<table id="table" class="display" style="width: 100%;">
<thead>
<tr>
{% for header in headers%}
<th>{{ header }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
{% for row in out%}
<td>{{row}}</td>
{% endfor %}
</tr>
</tbody>
</table>
I'm trying to turn this CSV into a table in my Django template. The headers are done correctly, but the rows are off. Is there a better way to read the rows so it'll go into tbody easier?
Seems that your problem is {% for row in out %}, you are passing {'data' : out}
Try:
<table id="table" class="display" style="width: 100%;">
<thead>
<tr>
{% for header in headers %}
<th>{{ header }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
{% for row in data %}
<td>{{ row }}</td>
{% endfor %}
</tr>
</tbody>
</table>

Not able to send checkbox data from all pagination in datatable from template to view in django

I have a datatable in my django template in which there is a checkbox next to every row to send the row data to a view function to make some mass updations in django model. But problem is if the multiple rows are on the same page in pagination then i can send the data accurately BUT if i select row 2 from page 1 and row 5 from page 3 only the row value from page 3 will be sent to the view function.!
TEMPLATE.HTML
{% block jquery %}
<script type="text/javascript" class="init">
$(document).ready( function ($) {
var $submit = $("#updiv").hide(),
$cbs = $('input[name="updelegate"]').click(function() {
$submit.toggle( $cbs.is(":checked") );
});
$('#myTable').DataTable({
dom: 'lBfrtip',
"pageLength": 1,
"language": {
"emptyTable": "No Delegates Available",
"sSearch": "Search Delegates: ",
"info": " Showing _START_-_END_ out of Total _TOTAL_ Delegates",
}
});
});
</script>
{% endblock %}
<form id="myForm" action="{% url 'mass-delegates' %}" method="POST">
{% csrf_token %}
<table id="myTable" class="table table-striped table-bordered" style="width:100%">
<thead class="thead-dark">
<tr>
<th></th>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Phone</th>
<th scope="col">Company</th>
<th scope="col">Designation</th>
<th scope="col">Address</th>
<th scope="col">City</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for del in delegates %}
<tr>
<td>
<label class="container">
<input type="checkbox" id="updelegate" name="updelegate"
value="{{ del.id }}">
<span class="checkmark"></span>
</label>
</td>
<td>{{ del.id }}</td>
<td>{{ del.first_name }} {{ del.last_name }}</td>
<td>{{ del.email }}</td>
<td>{{ del.phone }}</td>
<td>{{ del.company }}</td>
<td>{{ del.designation }}</td>
<td>{{ del.address }}</td>
<td>{{ del.city }} ({{ del.pincode }})</td>
<td>
View
</td>
<td>
Edit
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div id="updiv">
<select name="eventid">
{% for ev in events %}
<option value="{{ ev.id }}">{{ ev.name }}</option>
{% endfor %}
</select>
<input type="submit" onclick="return confirm('Confirm Adding to Event?');" class="upbtn"
name="update" value="Add to Event"/>
</div>
</form>
VIEW.PY
def mass_delegates(request):
if request.method == 'POST':
toupdate = request.POST.getlist('updelegate')
eventid = request.POST.get('eventid')
array_length = len(toupdate)
for i in range(array_length):
if not EventDelegate.objects.filter(event_id=eventid, delegate_id=toupdate[i]).exists():
EventDelegate.objects.create(event_id=eventid, delegate_id=toupdate[i])
return event_det(request, eventid)
Instead of using the checkboxes, you can directly use the select attribute in the Datatables api. Check Select Rows in Datatables
Moreover, Since you want to select multiple rows at once, you might consider checking Multi Select rows in Datatables out
You can add the id in the 0th column, then
dataTable = $(.selector).Datatable()
dataTable.columns([0]).visible(false);
You can hide the column like that and then when you send your request, you still have your id

How do I show/hide an entire column when a button is clicked?

I have a webapp and the results page that returns a table with multiple columns, I want my button to either hide or show the column.
I tried creating a function that does this and then call the function onclick
<button onclick="myFunction()">Show/Hide</button>
<script>
function myFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
</script>
this is the table it produces, and I want one of the columns to hide and show
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Table</th>
<th scope="col">Column</th>
<th scope="col">Description</th>
<th scope="col">Data Type</th>
<th scope="col">Length</th>
<th scope="col" id="myDIV">Data Stewards</th>
</tr>
</thead>
<tbody>
{% for r in results %}
<tr>
<td>{{r.tbl_name}}</td>
<td>{{r.col_name}}</td>
{% if r.FreeText!="nan" %}
<td>{{r.FreeText}}</td>
{% else %}
<td></td>
{% endif %}
<td>{{r.DataType_Name}}</td>
<td>{{r.DataType_MaxLength}}</td>
{% if r.tag_cat_name=="Data Stewards" %}
<td> {{r.tag_name}}</td>
{% else %}
<td></td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
in this format it only hides/shows the Data Stewards column heading, not the entire column
You could give the td, which you want to show/hide, the same class. Assuming, that you want to do that for the Data Stewards, it could look something like this:
HTML:
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Table</th>
<th scope="col">Column</th>
<th scope="col">Description</th>
<th scope="col">Data Type</th>
<th scope="col">Length</th>
<th scope="col" class="myDIVs">Data Stewards</th>
</tr>
</thead>
<tbody>
{% for r in results %}
<tr>
<td>{{r.tbl_name}}</td>
<td>{{r.col_name}}</td>
{% if r.FreeText!="nan" %}
<td>{{r.FreeText}}</td>
{% else %}
<td></td>
{% endif %}
<td>{{r.DataType_Name}}</td>
<td>{{r.DataType_MaxLength}}</td>
{% if r.tag_cat_name=="Data Stewards" %}
<td class="myDIVs"> {{r.tag_name}}</td>
{% else %}
<td class="myDIVs"></td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
JS:
function myFunction() {
const myDIVs = document.getElementsByClassName("myDIVs");
for (let i = 0; i < myDivs.length; i++) {
if (myDivs[i].style.display === "none") {
myDivs[i].style.display = "block";
} else {
myDivs[i].style.display = "none";
}
}
}

jinja2: building table by iterating down columns instead of across rows

If I have the following lists:
headers = ['Name', 'Age']
rows = [['Johnny', 30], ['Zack', 20]]
I can easily make a table via Jinja2 (https://jsfiddle.net/equbh9du/1/):
<table class="table table-bordered table-hover">
<thead>
<tr>
{% for h in headers %}
<td>{{ h }}</td>
{% endfor %
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
{% for item in row %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
I'm noticing it's much easier (and more organized) to return my data as a dictionary:
d = {'Name': ['Johnny', 'Zack'], 'Age': [30, 20]}
Is there an easy way to build the table I built above using this dict. I imagine I need to finish the iteration down each column before continuing to the next column (in the example above I finish the iteration across each row before continuing to the next row).
This is the code I have so far but I'm getting a messed up table (https://jsfiddle.net/j164fqy9/1/):
<table class="table table-bordered table-hover">
<thead>
<tr>
{% for h in d %}
<td>{{ h }}</td>
{% endfor %
</tr>
</thead>
<tbody>
{% for h, col_values in d.items() %}
{% for item in col_values %}
<tr>
<td>{{ item }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
EDIT: If HTML standard prevents iterating down columns first, then I need to construct the headers and rows from d. Is below the best way to do this?
headers = [h for h in d]
rows = [[l[i] for h, l in d.items()] for i in range(len(d['Name']))]
If you allways have the same number of items in col_values this should work fine :
{% for i in range(d['Name']|count) %}
<tr>
{% for k in d %}
<td>{{ d[k][i] }}</td>
{% endfor %}
</tr>
{% endfor %}

Changing a list output after a certain number of variables

I have a list:
c = [1,2,3,4,5,6,7,8]
in my template, I want to output this as follows:
<table>
<tr>
<td>1</td>
<td>2</td>
<td>...</td>
</tr>
</table>
<table>
<tr>
<td>5</td>
<td>...</td>
<td>8</td>
</tr>
</table>
What is the best way to do this?
If you'd like to make it more generic, you can also use the built in divisibleby tag
{% for value in c %}
{% if forloop.counter0|divisibleby:cut_off %}
<table>
<tr>
{% endif %}
<td>{{value}}</td>
{% if forloop.counter|divisibleby:cut_off %}
</tr>
</table>
{% endif %}
{% endfor %}
where c is the list and cut_off is the slicing number (e.g. 4 in your question). These variables are supposed to be sent the to the template in your view.
You can use slice template filter:
<table>
<tr>
{% for value in c|slice:":4" %}
<td>{{ value }}</td>
{% endfor %}
</tr>
</table>
<table>
<tr>
{% for value in c|slice:"4:" %}
<td>{{ value }}</td>
{% endfor %}
</tr>
</table>
Assuming c is passed in the template context.
slice basically follows usual python slicing syntax:
>>> c = [1,2,3,4,5,6,7,8]
>>> c[:4]
[1, 2, 3, 4]
>>> c[4:]
[5, 6, 7, 8]

Categories

Resources