forloop generating output for unordered list - django

I'm trying to generate an unordered list for my css tab interface. I've pretty much taken the example given to me and inserted the appropriate lines to generate the code, however the tab heading is only created once. I'm expecting 4 tab headings by using the forloop, which works if I use a standard table.
This is what the output ends up looking like: http://imgur.com/a/WVRXX
It's expected output looks like this: http://imgur.com/8xptd
Here is the HTML output that is generated: http://pastebin.com/jLf1cvYM
How can I get the forloop to generate my expected results? I'm at a loss here.
<div class="standard-tabs">
{% for vdev in pool.vdevs.all %}
{% for disk in vdev.disks.all %}
<!-- Tabs Headings -->
<ul class="tabs">
{% if forloop.first %}
<li>{{ vdev.name }}</li>
{% else %}
{% endif %}
</ul>
<!-- Tab Content -->
<div class="tabs-content">
<div id="#{{ vdev.name }}" class="with-padding">
{{ disk.serial }}
</div>
{% endfor %}
{% endfor %}
</div>
Here is the example I've been working off:
<!-- Wrapper, set tabs style class here -->
<div class="standard-tabs">
<!-- Tabs -->
<ul class="tabs">
<li class="active">Selected tab</li>
<li>Another tab</li>
<li>Another tab</li>
<li class="disabled">Disabled tab</li>
<li>Non-active</li>
</ul>
<!-- Content -->
<div class="tabs-content">
<div id="tab-1" class="with-padding">
Selected tab
</div>
<div id="tab-2" class="with-padding">
Alternate tab
</div>
<div id="tab-4" class="with-padding">
Disabled tab
</div>
</div>
</div>

This is only dirty fix.. It MAY not work but as I can see You have an error. Below corrected template:
<!-- language-all: lang-html -->
<div class="standard-tabs">
<!-- Tabs Headings -->
<ul class="tabs">
{% for vdev in pool.vdevs.all %}
{% if forloop.first %}
<li class="active">{{ vdev.name }}</li>
{% else %}
<li>{{ vdev.name }}</li>
{% endif %}
{% endfor %}
</ul>
<!-- Tab Content -->
<div class="tabs-content">
{% for vdev in pool.vdevs.all %}
<div id="#{{ vdev.name }}" class="with-padding">
{% for disk in vdev.disks.all %}
{{ disk.serial }} <br />
{% endfor %}
</div>
{% endfor %}
</div>
</div>

Related

How to customise task context (process_data cards) in django-viewflow

What I want
I'm building an approval workflow using django-viewflow and django-material.
The individual tasks are rendered as a main form with context on a very narrow column on the right-hand side.
I want to change the layout so that the task context (the detail views of all involved model instances) is better readable to the user, and also customise which fields are shown (eg. exclude a user's password hash).
Where I'm stuck
Is there a way to override which data is available as process_data short of overriding viewflow's get_model_display_data and include_process_data? E.g. I'd like to have the related instance's __str__() as title.
Does viewflow have any canonical way to provide individual detail card templates? My alternative would be to completely re-work the contents of the process_data sidebar using context['process'] as the central instance, but that would tie the templates to the data model.
Are django-material formsets with read-only fields and a custom layout the answer I'm after? (I will try this out as soon as I have a viewflow pro license.)
I'd be grateful on any pointers here.
What I've tried
I'm overriding/extending the viewflow templates. As per templatetag include_process_data, the template process_data.html supplies the column of model instance detail cards, fed by data from
get_model_display_data.
It's e.g. easy to override process_data.html to change the cards into a MaterializeCSS collapsible list:
{% load i18n viewflow material_frontend viewflow_frontend %}
<!--
This is template "APP_NAME/PROCESS_NAME/process_data.html" overriding "viewflow/flow/process_data.html".
We override instead of extending as "viewflow/flow/process_data.html" has no content block.super
Changes:
* Collapsible list of process context instead of cards.
-->
<script type="text/javascript">
$(document).ready(function () { $('.collapsible').collapsible(); });
</script>
<ul class="collapsible">
{% for root, fields, root_url in process_data %}
<li>
<div class="collapsible-header">
<span class="card-title">{{ root }} #{{ process.pk }}</span>
{% if root_url and request.user.is_staff %}
<a href="{{ root_url }}" class="card-edit" target="_blank" data-turbolinks="false" style="float:right">
{% trans 'edit' %}
</a>
{% endif %}
</div>
<div class="collapsible-body process_data_content">
<dl class="dl-horizontal">
{% for name, value in fields %}
<dt>{{ name }}:</dt>
<dd>
{% if value is True %}{% trans 'Yes' %}{% else %}
{% if value is False %}{% trans 'No' %}{% else %}
{% if value and value.url %}{{ value.name }}{% else %}
{{ value }}{% endif %}{% endif %}{% endif %}
</dd>
{% endfor %}
</dl>
</div>
{% if not hide_active_tasks and forloop.counter == 1 and process.active_tasks %}
<li>
<div class="collapsible-header">
<span class="card-title">{% trans 'Active tasks' %}</span>
</div>
<div class="collapsible-body process_data_content">
<table>
<thead>
<tr>
<th>{% trans 'Task' %}</th>
<th>{% trans 'Owner' %}</th>
</tr>
</thead>
<tbody>
{% for task in process.active_tasks %}
{% if task.flow_task.task_type == 'HUMAN' or task.flow_task.task_type == 'JOB' %}
<tr>
<td>
{{ task.flow_task}}/#{{ task.pk}}
</td>
<td>{{ task.owner|default:"" }}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</li>
{% endif %}
</li>
{% endfor %}
</ul>
Overriding the template viewflow/flow/task.html with my own APP_NAME/PROCESS_NAME/TASK_NAME.html template at the cost of including model-specific information can show e.g. customised cards of related instances over the main form. I would refactor the individual cards into their own templates.
{% extends "viewflow/flow/task.html" %}
{% block left-panel__top %}
<!--
This is template "APP_NAME/PROCESS_NAME/TASK_NAME.html" extending "viewflow/flow/task.html".
-->
<!-- Help specific to this step goes here. -->
{% with activation.process as p %}
<div class="row">
<h3>Nested formsets</h3>
<p>These cards are placeholders for formsets nested within the main application form.</p>
<p>Nested formsets and the main application form can be updated at the same time and have exactly one submission button.</p>
</div>
<div class="row">
<!-- Organisation Questions: Answers -->
<!-- TODO refactor to template include -->
{% for x in p.organisationanswer_set.all %}
<div class="col s12 m6 xl4">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">
{{ x.question.question }}
</span>
<p><strong>Your answer:</strong> {{ oa.answer }}</p>
</div>
<div class="card-action">
Provide an answer
</div>
</div>
</div>
{% endfor %}
<!-- Dataset Questions: Answers -->
{% for x in p.datasetanswer_set.all %}
<div class="col s12 m6 xl4">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">
{{ x.question.question }}
</span>
<p><strong>Your answer:</strong> {{ x.answer }}</p>
</div>
<div class="card-action">
Provide an answer
</div>
</div>
</div>
{% endfor %}
<!-- Approvals: Receipts - TODO show this in task after custodian approval -->
{% for x in p.approvalreceipt_set.all %}
<div class="col s12 m6 xl4">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">
{{ x.approval }}
</span>
<p><strong>Your approval receipt:</strong> {{ x.receipt }}</p>
</div>
<div class="card-action">
Provide an answer
</div>
</div>
</div>
{% endfor %}
</div>
{% endwith %}
<div class="row">
<h3>Main application form</h3>
<p>These main application form would include the forms above as nested formsets.</p>
<p>This note will disappear once this has been implemented.</p>
</div>
{% endblock %}
For the basic cases create a template named as [app_label]/[flow_label]/process_data.html For example check the shipment demo
Viewflow is the thin workflow layer on top of the standard Django model-view-template pattern. Any customization practices for Django are valid for Viewflow
For the complex cases it's better to not to tune some universal implementation, but create your own set of templates for Viewflow, ex cookbook/custom_ui

Recreating bootstrap cards row layout with tailwind in django

ASK
I'm trying to recreate bootstrap cards row layout with Tailwind within django framework
BLOCKER
However the tailwind attempt results in below
index.html -- bootstrap
{% extends 'base.html' %}
{% block title %}Home{% endblock title %}
{% block content %}
<div class="py-2">
<div class="row">
{% for t in object_list %}
<div class="col-md-3">
<div class="card mb-4 box-shadow bg-green-500">
<div class="card-body">
<h2 style="font-size:18px;font-weight:bold;min-height:42px;"><a class="text-dark" href="#">
{{t.currency|truncatechars:50}}</a></h2>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">{{t.country|truncatechars:25}}</small>
</div>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
{% endblock content %}
index.html -- tailwind
{% extends 'base.html' %}
{% block title %}Home{% endblock title %}
{% block content %}
<div class="p-10 ">
<div class="max-w-sm rounded overflow-hidden shadow-lg">
{% for t in object_list %}
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">{{t.country}}</div>
<p class="text-gray-700 text-base">
{{ t.currency }}
</p>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
You're starting the for loop a tag too late on the Tailwind version so all of your items are in a single card. And to recreate a 4 column grid layout in Tailwind I recommend using the grid utilities, specifically grid which is display: grid and grid-cols-4 which is grid-template-columns: repeat(4, minmax(0, 1fr)).
Your code might look like this:
{% extends 'base.html' %}
{% block title %}Home{% endblock title %}
{% block content %}
<div class="p-10 grid sm:grid-cols-2 md:grid-cols-4 gap-5">
{% for t in object_list %}
<div class="bg-green-500 rounded overflow-hidden shadow-lg">
<div class="px-6 py-4">
<div class="font-bold text-xl mb-2">{{t.country}}</div>
<p class="text-gray-700 text-base">
{{ t.currency }}
</p>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
Here's a visual of what the expected output would be https://play.tailwindcss.com/AWK45UcOug

Is it possible to apply if-else in django base.html?

My all pages are inheriting from base.html file. However I want my about page to be slightly different.
Majority of the pages are having
<main role="main" class="container">
<div class="row">
<div class="col-md-8">
<div>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
<div class="col-md-4">
<div class="content-section">
<h3>Our Sidebar</h3>
<p class='text-muted'>You can put any information here you'd like.
<ul class="list-group">
<li class="list-group-item list-group-item-light">Latest Posts</li>
<li class="list-group-item list-group-item-light">Announcements</li>
<li class="list-group-item list-group-item-light">Calendars</li>
<li class="list-group-item list-group-item-light">etc</li>
</ul>
</p>
</div>
</div>
</div>
</main>
But about page I want the above code of block like
<main role="main" class="container">
<div>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
</main>
So is there a way the second block of code is only executed when the page is "about.html"
else the first block should be executed.
I can do these changes if I do not inherit about from base.html, but then I will be repeating a lot of lines of code. That is why I am looking for another approach
EDIT
wondering if something like "{% if url == 'blog-about' %}" exists
If you don't want the sidebar to not appear, you can create another html file called sidebar.html and insert the sidebar code, and another html file called message.html and insert the message template.
Then include this tag in all of your pages {% include sidebar.html %} and {% include message.html %} and leave it for your about.html.

How to pass one django template with a 'for loop' into another one using 'include' statement?

I am working on FAQs page where questions and answers are passed to a template sections based on their categories. I would like to reduce amount of html and use section div as a template
<div id="{{id}}">
<div class="h2">{{category}}</div>
{% for q in faqs %}
{% if q.category == '{{category}}' %}
<ul class="collapsible">
<li>
<div class="collapsible-header">{{q.question}}></div>
<div class="collapsible-body"><span>{{q.answer}}</span></div>
<div class="divider"></div>
</li>
</ul>
{% endif %}
{% endfor %}
</div>
My main html contains following code:
{% with id='m_faq'%}
{% with category='Methodology'%}
{% include 'main/faqs_section.html' %}
{% endwith %}{% endwith %}
I am only able to pass variables id and category.
Is there a way to the for loop as well?
I think the solution would be to create a list for categories in views.py
cat = [ 'Category1', 'Category2', 'Category3','Category4']
pass it to context dictionary and then put additional 'for loop' around section div.
{% for c in cat %}
<div id="">
<div class="h4">{{c}}</div>
{% for q in faqs %}
{% if c == q.category %}
<ul class="collapsible">
<li>
<div class="collapsible-header">{{q.question}}</div>
<div class="collapsible-body"><span>{{q.answer}}</span></div>
<div class="divider"></div>
</li>
</ul>
{% endif %}
{% endfor %}
</div>
{% endfor %}
That will generate a template with list of faqs divided in to sections..

Jinja2: Create new row for every 3 items

Currently for every article in articles a new div with class span4 is created.
Instead for each row I would like to limit it's content to three span's and create a new row once that limit has been reached. How can I best implement this?
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<legend></legend>
<div class="row-fluid" id="main">
{% for article in articles %}
<div class="span4">
<div class="thumbnail">
<img src="http://placehold.it/300x150/483CB4">
<div class="caption">
<h4>{{ article.title }}</h4>
<p> {{ article.summary }}</p>
</div>
<legend></legend>
<a class="btn" href="#"><i class="icon-thumbs-up"></i></a>
<a class="btn" href="#"><i class="icon-thumbs-down"></i></a>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
Goal:
<div class="row">
<div class="span4">article[0]</div>
<div class="span4">article[1]</div>
<div class="span4">article[2]</div>
</div>
<div class="row">
<div class="span4">article[3]</div>
<div class="span4">article[4]</div>
<div class="span4">article[5]</div>
</div>
The best way to do this is to use the built in batch filter to break up your list into groups of three:
{% for article_row in articles | batch(3, ' ') %}
<div class="row">
{% for article in article_row %}
<div class="span4">{{ article }}</div>
{% endfor %}
</div>
{% endfor %}
You can use loop.index0 and loop.last inside the for loop. Here is the for-loop documentation.
Example:
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<legend></legend>
<div class="row-fluid" id="main">
{% for article in articles %}
{% if loop.index0 % 3 == 0 %}
<div class="row">
{% endif %}
<div class="span4">
...
</div>
{% if loop.index0 % 3 == 2 or loop.last %}
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}
The loop.last clause should close the last row even if there were less than 3 items. <div> should start when loop.index0 gives 0 as the remainder and should end when 2 is the remainder
Another alternative would be to group the items into rows before passing them into the template, then you can just issue two for-loops one inside the other.