Flask - Making HTML table of hyperlinks via nested for-loops? - html

I'm designing a Flask application that works with a MySQL database.
I have this Flask code below:
#app.route("/test")
def test():
cursor.execute("SELECT * from testtable;")
data = cursor.fetchall()
return render_template('test.html', data = data)
I wish to make an HTML table from this data, and I want the first column of this table to be hyper-linked. My current test.html is shown below:
<table border="1" cellpadding="5" cellspacing="5">
{% for row in data %}
<tr>
{% for d in row %}
<td>{{ d }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
This HTML makes a hyper-link out of every cell in every column of the table. Is there a way to make only the cells in the first column be hyper-linked, and make all other cells just show {{ d }}?

The default template engine in Flask is jinja2.
In jinja2 you can check the loop index, which means that you could do something like the following.
{% for d in row %}
{% if loop.index == 1 %} # You can also use loop.index0 for 0-based indexing
<td>{{ d }}</td>
{% else %}
<td>{{ d }}</td>
{% endif %}
{% endfor %}
You can also skip the first element in the row list by using the following syntax:
{% for d in row[1:] %}

In your table, put for for loop outside the tr element.
<table border="1" cellpadding="5" cellspacing="5">
{% for row in data %}
<tr>
<td>{{ row.d }}</td>
{% endfor %}
</tr>
</table>

Related

Reusable jinja2 table

I want to use the template to create all the tables of my project, but after writing is appearing errors like this:
jinja2.exceptions.TemplateSyntaxError: expected token ':', got '}'
{{ table(headers={{headers}},items={{item}},url='None') }}
I've looked at the jinja2 website but couldn't find the answer to the syntax error.
# python
#app.route('/products')
def products():
context = {}
qproducts = list(s.query(Product))
context['products'] = qproducts
return render_template('products.html', **context)
# table.html
{% macro table(headers,items,url,var) -%}
<table class='table table-sm table-dark'>
<thead>
<tr>
{{headers}}
</tr>
</thead>
<tbody>
{% for item in items %}
<tr onclick="window.location='{{url}}'">
{{items}}
</tr>
{% endfor %}
</tbody>
</table>
{%- endmacro %}
# products.html
{% from 'table.html' import table %}
{% block headers %}
<th>ID</th>
<th>Price</th>
{%endblock headers%}
{%block item%}
{%for item in products%}
<td>{{item.id}}<td><br>
<td>{{item.price}}<td><br>
{%endfor%}
{%endblock item%}
{{ table(headers={{headers}},items={{item}},url='None') }}
Even when you fix the references to variables (i. e. remove surrounding {{/}}) it won't work the way you expect. block tag can only be used with extends tag, but you use import for macros importing. If you want to write universal macro for table rendering it's better to use combination of macro/call tags:
{% macro table(headers,items,url,var) -%}
<table class='table table-sm table-dark'>
<thead>
<tr>
{{ caller(mode='header') }}
</tr>
</thead>
<tbody>
{% for item in items %}
<tr onclick="window.location='{{url}}'">
{{ caller(mode='row', item) }}
</tr>
{% endfor %}
</tbody>
</table>
{%- endmacro %}
caller here is a reference to a special function which invokes externally-provided callback. Then you can call this macro this way:
{% call(mode, items) table(headers=headers, items=item, url='None') %}
{% if mode='header' %}
<th>ID</th>
<th>Price</th>
{% else %}
{%for item in products%}
<td>{{item.id}}<td><br>
<td>{{item.price}}<td><br>
{% endfor %}
{% endif %}
{% endcall %}
Every mention of caller in macro table invokes body of the caller tag with the specified parameters. So, you can customize the behavior of the macro.

How to add if function in django html template

I want to add an if function in an html template of my django project.
If the variables datas is null, it will show the text "The results is null", if the variables datas is not empty, it will show the table for the data in datas.
Here's what I write, but it raises the error
Invalid block tag on line 13: 'if(isEmpty($('#datas')))', expected 'endblock'.
Did you forget to register or load this tag?
How could I deal with it?
{% if(isEmpty($('#datas'))) %}
<h3>The results is null.</h3>
{% else %}
<table style="table-layout:fixed;">
<tr>...</tr>
<tr>
{% for i in datas %}
<td>{{ i.1 }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
I think you can use empty tag. Use it like this:
<table style="table-layout:fixed;">
<tr>...</tr>
<tr>
{% for i in datas %}
<td>{{ i.1 }}</td>
</tr>
{% empty %}
<h3>The results is null.</h3>
{% endfor %}
But, if you want to check if datas is empty beforehand, then I think its best to do it in views. You can try like this:
def some_view(request):
context = {}
datas = Data.objects.all()
if datas.exists():
context['data_exists'] = True
context['datas'] = datas
return render(request, 'template.html', context)
and use it template:
{% if not data_exists %}
<h3>The results is null.</h3>
{% else %}
....
You can directly use if condition
if you are sending datas from a view
view
def index(request):
datas = None
context = {"datas":datas}
return render(request, "index.html", context)
template
{% if datas %}
<!-- your table -->
{% else %}
<h3>The results is null</h3>
{% endif %}

how to iterate over a list of list in jinja

I have a list of list like :
[[elem0, elem1, elem2], [elem3, elem4, elem5], [elem6, elem7, elem8], ...]
I wrote the follow template file :
{% for result in results %}
<tr>
<td>result[0]</td>
<td>result[1]</td>
<td>result[2]</td>
</tr>
{% endfor %}
But it didn't work, What i can think is use nested for. Is there another method to access the element in the list in jinja?
You still need to output the loop variables inside braces.
{% for result in results %}
<tr>
<td>{{ result[0] }}</td>
<td>{{ result[1] }}</td>
<td>{{ result[2] }}</td>
</tr>
{% endfor %}
Also, consider a nested for loop:
{% for result in results %}
<tr>
{% for elem in result %}
<td>{{elem}}</td>
{% endfor %}
</tr>
{% endfor %}

HTML table formatting with django

I'm creating an e-commerce website and I wanted to build a page where I could view a list of all the orders created.
If the order contains just 1 type of item, the format works correctly, but i can't think of a way to construct the table when there are multiple types of items ordered.
This is how it looks like when there are 2 items ordered (last entry):
I want the "Queso Burrito" to be right under "steak and egg burrito" for #18.
This is my code:
<table>
<tr>
<td>#</td>
<td>Name</td>
<td>Email</td>
<td>Phone</td>
<td>Order</td>
<td>Order Quantity</td>
<td>Delivered</td>
</tr>
{% for ord in orders %}
<tr>
<td>{{ord.pk}}</td>
<td>{{ord.user.first_name}}</td>
<td>{{ord.user.email}}</td>
<td>{{ord.user.get_profile.phone}}</td>
{% for food in ord.orderitem_set.all %}
<td>{{food.name}}</td>
<td>{{food.quantity}}</td>
{% endfor %}
<td>x</td>
</tr>
{% endfor %}
</table>
With multiple items, you typically see tables with order data repeated for each line item.
{% for order in orders %}
{% for orderitem in order.items %}
<td>{{order.id}}</td><td>...</td>
{% endfor %}
{% endfor %}
If you want exactly the formatting you described, you could check if the inner loop is past its first item and hide the fields you don't want repeated.
<table>
{% for ord in orders %}
{% for item in ord.orderitem_set.all %}
<tr>
{% if forloop.counter == 1 %}
<td>{{ord.pk}}</td>
<td>{{ord.user.first_name}}</td>
<td>{{ord.user.email}}</td>
<td>{{ord.user.get_profile.phone}}</td>
{% else %}
<td colspan="4"></td>
{% endif %}
<td>{{item.name}}</td>
<td>{{item.quantity}}</td>
<td>{% if forloop.counter == 1 %}x{% endif %}</td>
</tr>
{% endfor %}
{% endfor %}
</table>

how to number t-rows ,when table generated using nested forloop in django templates

This part is from views.py
results=[(A,[stuObj1,stuObj2,stuObj3]),(B,[stuObj4,stuObj5,stuObj6]),(C,[stuObj7,stuObj8])]
for tup in results:
total = tot+len(tup[1])
render_to_response(url,{'results':res , 'total':str(tot),})
this is template code:
<th class="name">Name</th>
<th class="id">Student ID</th>
<th class="grade">Grade</th>
{% for tup in results %}
{% for student in tup|last %}
{% with forloop.parentloop.counter as parentloopid %}
{% with forloop.counter as childloopid %}
<tbody class="results-body">
<tr>
<td>{{student.fname|lower|capfirst}} {{student.lname|lower|capfirst}}</td>
<td>{{student.id}}</td>
<td>{{tup|first}}</td>
</tr>
{% endfor %}
{% endfor %}
Now the problems am having are
numbering the rows. Here my problem is am not sure if i can do things like total=total-1 in the templates to get the
numbered rows like <td>{{total}}</td>
applying css to tr:even or odd.
Whats happening in this case is everytime the loop is running the odd/even ordering is lost.
these seems related problems. Any ideas would be great :)
For numbering the rows you can use the forloop.counter
Here you can see an example how to use it.
To switch between even and odd you can use the cycle template tag
{% for project in object_list %}
<tr class="{% cycle odd,even %}"> ... </tr>
{% endfor %}
answer for my question:
Numbering would be a bit time taking. one option is to design custom filters or other way is to modify views and use simple forloop.counter to add, count and forloop.counter. Let me give an example: for the above cases, results are sorted dictionaries with grades and students,something like this ((A:a,b,c,d,e), (B:f,g,h,i), (C:j,k,l,m,n)). In the view add one more dictionary to each tuple with the student count of previous tuple.
temp_count = 0
for tup in results:
tup[1].append({'count':temp_count})
temp_count = temp_count + len(tup[1])-1
-1 is because we don't want to avoid dictionary counted
Inside the template
{% with tup|last|last as cnt %}
{% with forloop.counter as rnum %}
{% with rnum|add:cnt.count as rownum %}
<td>{{rownum}}</td>
rest of code goes here
{% endwith %}
{%endwith%}
{% endwith %}
using {% cycle %} won't be that helpful when using nested loops,
<tr class="{% if rownum|divisibleby:2 %}"even"{% else %}"odd"{% endif %}">
follow this clean way to color.