Django-tables2 render merged tables - python

I use django-tables2 and have following table, which is populated by generated dictionary:
class SimpleStatTable(tables.Table):
Metric = tables.Column(accessor='metric')
Qty = tables.Column(accessor='qty')
Min = tables.Column(accessor='min')
Avg = tables.Column(accessor='avg')
Max = tables.Column(accessor='max')
def __init__(self, data, label=None, **kwargs):
self.label = label
super(SimpleStatTable, self).__init__(data, **kwargs)
class Meta:
order_by = ('Metric',)
empty_text = 'No data presented'
I want to render merged table of a few SimpleStatTable tables. Is it possible with django-tables2? I like django-tables2 features, such as sorting
I have a small example of desired tables.
I suppose i need to generate class for merged table dynamically like here, but how can i add additional merged column?

I have a solution, but it's not elegant.
I merge two dictionaries and build table for merged dictionary. After that i just add merged columns to the table. They are in same order as in merged dictionary. Also I've changed template for the table for handling merged columns.
Here is the code:
from collections import defaultdict
import django_tables2 as tables
def merge_table_dicts(labels, tables, key_column):
merged_tables = []
for table, label in zip(tables, labels):
new_table = []
for row in table:
new_row = {}
for key, value in row.iteritems():
if key == key_column:
new_row[key] = value
else:
new_row[old_key + '_' + label] = value
new_table.append(new_row)
merged_tables.append(new_table)
d = defaultdict(dict)
for l in merged_tables:
for elem in l:
d[elem[key_column]].update(elem)
merged_table_dicts = d.values()
return merged_table_dicts
def get_merged_table(labels, tables_dicts, key_column_name, merged_columns_order):
attrs = {}
# General options
class Meta:
order_by = (key_column_name,)
template = '_merged_table.html'
empty_text = 'No data presented'
attrs['Meta'] = Meta
attrs[key_column_name] = tables.Column()
for column in merged_columns_order:
for label in labels:
attrs[get_merged_key(column, label)] = tables.Column(verbose_name=label)
merged_table = type('MergedTable', (tables.Table,), attrs)
merged_columns = []
for merged_column_name in merged_columns_order:
column = tables.Column(verbose_name=merged_column_name,
attrs={"th": {"rowspan": len(labels)}})
merged_columns.append(column)
merged_table.merged_columns = merged_columns
# Merge data for table
data = merge_table_dicts(labels, tables_dicts, key_column_name)
return merged_table(data)
And it's a changed part of a template:
<thead>
<tr>
{% with column=table.columns|first %}
{% if column.orderable %}
<th rowspan="2" {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% else %}
<th rowspan="2" {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% endif %}
{% endwith %}
{% for column in table.merged_columns %}
<th colspan="{{ column.attrs.th.rowspan }}">{{ column.header }}</th>
{% endfor %}
</tr>
<tr>
{% for column in table.columns %}
{% if not forloop.first %}
{% if column.orderable %}
<td {{ column.attrs.th.as_html }}>{{ column.header }}</td>
{% else %}
<td {{ column.attrs.th.as_html }}>{{ column.header }}</td>
{% endif %}
{% endif %}
{% endfor %}
</tr>
</thead>

Related

Django views.py Exception Value: Could not parse the remainder:

I get an Exception Value error:
Could not parse the remainder: '(column)' from 'item.get(column)'
views.py:
def home(request):
position = DjangoEmail.objects.get(Email=request.user).Position
year_filter = Q(Year=now.year) | Q(Year=now.year-1) | Q(Year=now.year+1)
if position == 7:
data = Employee.objects.filter(year_filter, Mlkk=request.user).order_by('Year','OblastTM').values('Year', 'OblastTM', 'Category', 'ProductGroup','NameChaine').annotate(Januaru=Sum('January'))
elif position == 6:
data = Employee.objects.filter(year_filter, Rmkk=request.user).order_by('Year','OblastTM').values('Year', 'OblastTM', 'Category', 'ProductGroup','NameChaine').annotate(Januaru=Sum('January'))
elif position == 5:
data = Employee.objects.filter(year_filter, Dmkk=request.user).order_by('Year','OblastTM').values('Year', 'OblastTM', 'Category', 'ProductGroup','NameChaine').annotate(Januaru=Sum('January'))
else:
data = Employee.objects.filter(year_filter).order_by('Year','OblastTM').values('Year', 'OblastTM', 'Category', 'ProductGroup','NameChaine').annotate(Januaru=Sum('January'))
columns = ['Year', 'OblastTM', 'Category', 'ProductGroupe', 'NameChaine','January']
removed_columns = request.GET.getlist('remove')
columns = [column for column in columns if column not in removed_columns]
return render(request, "home.html", {'data': data, 'columns': columns})
home.html:
<table>
<thead>
<tr>
{% for column in columns %}
<th>{{ column|title }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in data %}
<tr>
{% for column in columns %}
<td>{{ item.get(column)}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
error:
Exception Value:Could not parse the remainder: '(column)' from 'item.get(column)'
Error in the line :
<td>{{ item.get(column)}}</td>
I tried to replace it with {{ item\[column\] }} - it didn't help.
You can not subscript or call methods in Django templates, hence {{ item.get(column) }} is not possible. This is often not a good idea anyway: you should pass the data in an accessible format to the template.
You thus prepare this as:
from operator import itemgetter
def home(request):
position = get_object_or_404(DjangoEmail, Email=request.user).Position
year_filter = Q(Year__range=(now.year - 1, now.year + 1))
columns = [
'Year',
'OblastTM',
'Category',
'ProductGroupe',
'NameChaine',
'Januaru',
]
removed_columns = set(request.GET.getlist('remove'))
columns = [column for column in columns if column not in removed_columns]
queryset = Employee.objects.filter(year_filter)
if position == 7:
queryset = queryset.filter(Mlkk=request.user)
elif position == 6:
queryset = queryset.filter(request.user)
elif position == 5:
queryset = queryset.filter(Dmkk=request.user)
queryset = (
queryset.order_by('Year', 'OblastTM')
.values('Year', 'OblastTM', 'Category', 'ProductGroupe', 'NameChaine')
.annotate(Januaru=Sum('January'))
)
if columns:
getter = itemgetter(*columns)
if len(columns) == 1:
data = [(getter(data),) for data in queryset]
else:
data = [getter(data) for data in queryset]
else:
data = ((),) * queryset.count()
return render(request, 'home.html', {'data': data, 'columns': columns})
then we can render this with:
<thead>
<tr>
{% for column in columns %}
<th>{{ column|title }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
{% for cell in row %}
<td>{{ cell }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>

multiple python lists to table rows in HTML

I have multiple lists created in python. I am trying to convert these lists into HTML rows.
week = ['1','2']
date = ['2022-10-01','2022-10-09']
My HTML table should look like below:
Week Date
1 2022-10-01
2 2022-10-09
what I tried so far:
{% for val in data %}
<tr>
<td>
{% for item in val.week %} {{ item }} <br> {% endfor %}
</td>
<td>
{% for item in val.date %} {{ item }} <br> {% endfor %}
</td>
</tr>
{% endfor %}
The problem with above code is every value is treated as cell instead of row and hence I am unable to apply any row related styles to the above.
Could someone help with how to convert these to rows instead of cells.
Thank you.
You can use dictionary comprehension to convert the two lists into a dictionary where the key would be the week number and the value would be the corresponding date. You can loop over the dictionary as key and value using the items attribute in the template.
# views.py
week = [1, 2]
date = ['2022-10-01','2022-10-09']
data = {week[i]: date[i] for i in range(len(week))}
return render(request, 'template.html', {'data': data})
# template.html
{% for key, value in data.items %}
<tr>
<td> {{ key }} </td>
<td> {{ value }} </td>
</tr>
{% endfor %}
try this approch...
------ views.py ------
week = ['1','2']
date = ['2022-10-01','2022-10-09']
data = tuple(zip(week,date))
------ html ------
<table style="border: 1px solid; border-collapse: collapse;">
<thead>
<th>Weeks</th>
<th>Dates</th>
</thead>
<tbody>
<tbody>
{% for i,j in data %}
<tr>
<td>{{i}}</td>
<td>{{j}}</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>
-------- Output -------

How to create a pagination for a dataframe?

I want to create a pagination system for my dataframe. I always create pagination with my queries but It doesn't work in dataframe. I cannot display on my template.
This is my views.py
def get_context_data(self, **kwargs):
...
df = pd.DataFrame(list(Case.objects.all().values(...)))
query = df.assign(cnt=1).groupby([...)['cnt'].sum().reset_index()
paginator = Paginator(query, 10)
page_number = self.request.GET.get('page')
if page_number == None:
page_number = 1
page_obj = paginator.page(int(page_number))
print(page_obj) ----> <Page 1 of 33>
context['query'] = query
return context
And this is my template
<table class="table table-bordered" >
<tbody>
{% for index, row in query.iterrows %}
<tr>
{% for cell in row %}
<td>
{{cell}}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Table shows nothing. How can I solve that?

How to properly unpack a nested dictionary in a django HTML template?

So I was able to figure out how to unpack a dictionary keys and values on to a HTML template, but I am a bit confused as to how to unpack if if a dictionary value is a QuerySet. For example I passing in all the timeslots of the given user into the dictonary. How can I unpack the attributes of the TimeSlot QuerySet for each timeslot such as the start time and end time?
This is my HTML Template:
<table>
{% for key, value in user_info.items %}
{% for key2,value2 in value.items %}
<tr>
<td>{{key2}}</td>
<td>{{value2}}<td>
</tr>
{% endfor %}
<br>
{% endfor %}
</table>
My function in views.py
def check_food_availibility(request):
food = FoodAvail.objects.all()
timeslots = TimeSlot.objects.all()
users_dict = {}
for i in range(len(user_info)):
user = {
"author": None,
"food_available": None,
"description": None,
"timeslot": None
}
user["author"] = user_info[i].author.username
user["food_available"] = user_info[i].food_available
user["description"] = user_info[i].description
if TimeSlot.objects.filter(time_slot_owner=user_info[i].author):
user["timeslot"] = TimeSlot.objects.filter(time_slot_owner=user_info[i].author)
users_dict[user_info[i].author.username] = user
return render(request, "food_avail/view_food_avail.html", {"user_info": users_dict})
This is how it shows up currently:
try this
<table>
{% for key, value in user_info.items %}
{% for key2,value2 in value.items %}
<tr>
<td>{{key2}}</td>
{% if key2 == 'timeslot' %}
<td>
{% for i in value2 %}
i.start_time <-> i.end_time // just an example put your field here
{% endfor %}
</td>
{% else %}
<td>{{value2}}<td>
{% endif %}
</tr>
{% endfor %}
<br>
{% endfor %}
</table>

Optimize code in Django - view ManyToMany as matrix

I'm trying to show user group permissions in Django and show them in a "Drupal" style like a matrix. It works, but it takes too long to make the query and paint it in the template. Is there some way to improve my code?
view img
up(accomplished),down(views and template.html)
views :
def GroupPermissionsView(request):
title = "Groups Permissions"
groups = Group.objects.all()
permissions = Permission.objects.all()
context = Context({
'title': title,
'groups': groups,
'permissions': permissions,
})
return render(
request,
'forms_permissions.html',
context
)
template:
<table class="table table-striped table-inverse table-responsive table-bordered">
<thead>
<tr>
<th>Permission</th>
{% for group in groups %}
<th>{{ group.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for permission in permissions %}
<tr>
<td><b>{{permission.name}}<b></td>
{% for group in groups %}
{% if permission in group.permissions.all %}
<td><input type="checkbox" name="" checked="checked"></input></td>
{% else %}
<td><input type="checkbox" ></input></td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
Your problem was that you run more than 4 * 200 queries, one query for every combination or rows and columns (permissions and groups). It is useful to get them all by one query. It is however not easy because the intermediate model of ManyToMany relationship between Permission and Group models is not explicit in django.contrib.auth.models. You can get that model by Model._meta API:
>>> GroupPermissions = Permission._meta.get_field('group').through
>>> GroupPermissions
<class 'django.contrib.auth.models.Group_permissions'>
>>> GroupPermissions._meta.db_table # the table that you use in the raw query
'auth_group_permissions'
Put it all together. Prefer a longer view and simple template:
update the view:
from collections import OrderedDict
GroupPermissions = Permission._meta.get_field('group').through
groups = Group.objects.all()
permissions = Permission.objects.all()
permission_group_set = set()
for x in GroupPermissions.objects.all():
permission_group_set.add((x.permission_id, x.group_id))
# row title and cells for every permission
permission_group_table = OrderedDict([
(permission, [(permission.id, group.id) in permission_group_set for group in groups])
for permission in permissions
])
context = Context({
'title': title,
'groups': groups,
'permission_group_table': permission_group_table,
})
update the template
{% for permission, cells in permission_group_table.items %}
<tr><td><b>{{permission.name}}<b></td>
{% for cell in cells %}
{% if cell %}
<td><input type="checkbox" name="" checked="checked"></input></td>
{% else %}
<td><input type="checkbox" ></input></td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}

Categories

Resources