Breaking data into multiple display columns with Django - django

Overlap in terminology makes search for answers difficult for this one.
I'm looking for advice on the best way to implement a multiple-column display of my QuerySet that fills each column top to bottom over X columns. Meaning that the number of items in each column equals the QuerySet count divided by X (number of columns).
Using Offset doesn't work for this because I would like my data to grow into 4 columns without updating the offset manually. CSS floats work visually, but leave the data out of order.

Something like that should work for you, pass the number of columns as columns to the template:
{% for item in items %}
{% if forloop.first %}<div style="float:left;">{% endif %}
{{ item }}
{% if forloop.counter|divisibleby:columns %}
</div><div style="float:left">
{% endif %}
{% if forloop.last %}</div>{% endif %}
{% endfor %}
<div style="clear:both;"></div>

It seems like lazerscience's answer is on the right track - but I think the OP wants the data to alphabetically sort the data down the column and then start back at the top of the next column. Using divisibleby: works to break the line after 'X' items, but I think it'll take a more complex template to do what the OP wants.
Not sure if it's possible, but in the view could you: Get item count, divide by 'columns' and used that # in Divisibleby to break into the next DIV column (the visual flow will be CSS)
As Lazers's example is now you're constructing Rows and then breaking it into columns, leaving the sort order across and then down.
Sorry if I missed something.
-K

You'd better go and use a jQuery plugin to make some columns from a list.
Columnizer works very well for me

Here are some django template filter that split a list into multiple sub-lists:
list partition template filters at djangosnippets.org
You could use these in a django template to split a long list into multiple columns as follows:
{% load list_tags %}
<h2>Some List</h2>
{% for sub_list in full_list|rows:"3" %}
<ul>
{% for item in sub_list %}
<li>
{{item.name}}
</li>
{% endfor %}
</ul>
{% endfor %}
I included the template filters in my project in a file called list_tags.py. Adjust the {% load %} tag as necessary.

Related

Django templating: extend a block in same template?

So here is the set up (simplified for here):
In my template file I have two sections I would like to add content too by looping of a list of dictionaries, e.g.:
<div class="stuff-a"></div>
<div class="stuff-b"></div>
{% for thing in list %}
<!-- goes into div a -->
<!-- goes into div b -->
{% endfor %}
I thought this would work:
<div class="stuff-a">
{% block diva %}{% endblock %}
</div>
<div class="stuff-b">
{% block divb %}{% endblock %}
</div>
{% for thing in list %}
{% block diva %} thing.stuff_for_div_a {% endblock %}
{% block divb %} thing.stuff_for_div_b {% endblock %}
{% endfor %}
and then django complains about me using each block more than once - shame on me.
Now the solution to this (keeping the same schema) is to make an intermediate template file and then extend it.
My question is, is there a way to append to different specific areas in a django template without having to loop through a list twice or an intermediate template file?
This matters if the loop is somewhat complicated and you dont want to have to update two loops whenever you make a change.
When might this occur in practice (outside this example)? If you have a list where each element in a list references another list (e.g. the way bootstrap handles tabnation)
Now the solution to this (keeping the same schema) is to make an intermediate template file and then extend it.
You can't do this either because you can't use {% block %} in a loop for the same reason as you can't have a block more than once in the same template.
My question is, is there a way to append to different specific areas in a django template without having to loop through a list twice or an intermediate template file?
No you can't. And if ever you could do what you are trying to achieve, it would necessarily involve looping twice over the list behind the scenes.
This matters if the loop is somewhat complicated and you dont want to have to update two loops whenever you make a change.
It matters indeed if the loop is a generator where the next element is calculated at each iteration. But in this case you wouldn't be able to loop twice over the iterable anyway, which is necessary.
If it is a list already calculated and stored in memory, it doesn't matter.
When might this occur in practice (outside this example)? If you have a list where each element in a list references another list (e.g. the way bootstrap handles tabnation)
I don't see how this would be complicated.

How do I remove default whitespace in template?

I'd like to list a series of items separated by commas.
But if an item after the first doesnt exist, I don't want the comma or item to appear.
So I've implemented this:
<em>{{object.item1|default_if_none:""}}</em>
{% if object.item2 %}
<em>, {{object.item2|default_if_none:""}},</em>
{% endif %}
<em>{{object.item3|default_if_none:""}}</em>
If object.item2 exists, it'll put a whitespace after item1--before the comma.
When it displays, it'll look something like:
"I_am_item_one , I_am_item_two, Item_three"
What's the best way to fix this?
edit: is it possible to for-loop through an object's properties?
{% for property in object.property %} or similar..
I am not aware of any way to loop over objects and their properties in a template. You could look into either passing the data into the template as a dictionary/list or creating a model method (as done in this answer: Django templates: loop through and print all available properties of an object?)
Once your data is iterable, you could use the following code to decide when to add a comma. I am not entirely clear if you want a comma between each item or only after the first item, so here are two options.
This will only add a comma on the first entry
{% for item in item_list %}
<em>{{ item }}</em>{% if forloop.first %}, {% endif %}
{% endfor}
This will add a comma after each item except the last one
{% for item in item_list %}
<em>{{ item }}</em>{% if not forloop.last%}, {% endif %}
{% endfor}
It is probably fairly clear, but you can use forloop.first to detect when you are on your first item and forloop.last to detect when you are on the last item of a loop.

Parsing and using list item content via Twig

I am experimenting with using Grav to create my next website. One of the things I would be able to do is to build a unordered list using data provided in Grav frontmatter from the Grav page that uses the template. Here is how I am trying to do this
---
sectionone:
listitems: "['Benefit 1','Benefit 2','Benefit 3']"
---
and then in the template somehow do the following
{% block featurelist %}
<ul>
{% set items = {{page.header.sectionone.consumers.benefits|json_decode}} %}
{% for item in {{}} %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
However, Twig does not like this and reports back an error along the lines of
Twig_Error_Syntax
A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "punctuation" of value "{".
with the offending line being my {% set items = ... } statement. I am clearly doing something wrong here but I am a Twig newbie so I fail to see what that might be.
{% block featurelist %}
<ul>
{% set items = page.header.sectionone.consumers.benefits|json_decode %}
{% for item in items %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
I figured this out eventually. Grav documentation is by and large very good. However, the documentation on page headers/frontmatter appears to be somewhat incomplete. There is no full description of the entire syntax that is understood by the frontmatter processor. In order to define an array in front matter all you need to do is the following
---
sectionone:
benefits:
- 'Benefit 1'
- 'Benefit 2'
- ...
---
In essence the standard markdown syntax for an unordered list. Grav's twig processor appears to convert this to a PHP array - no parsing required!

Adding an if statement to django-cms template tag

I'm probably missing something very obvious but can't see anything in the documentation. I have a carousel with a and each will hold an image. However I've added 6 but I want to add an if statement so if an Image has not been added you don't see a blank space, where there is no content inside the .
Here is what i've tried so far:
{% if "Carousel 1" %}
<li>
{% placeholder "Carousel 1" %}
</li>
{% endif %}
Attempt 2:
{% placeholder "Carousel 1" as cara1 %}
{% if cara1 %}
<li>
{{ cara1 }}
</li>
{% endif %}
Not sure if there is something differnt i need to be doing for the django-cms template tags?
Any help would be much appreciated. Docs here - http://docs.django-cms.org/en/latest/advanced/templatetags.html#placeholder
Not to be rude, but your approach is way, way off :)
Placeholders hold Content Plugins. Content Plugins are responsible for how they render their contents.
My advice would be to create or find a carousel content type plugin. This plugin will hold multiple images or "CarouselImage" model instances that you can iterate over, and also specify a template with which to render itself.
In this template resides the conditional statement you're wanting to check for. Placeholders are just that - places held for content plugins.

Django tables2 set default sort to be descending

When django-tables2 renders an unsorted table, and I want to sort by one column, I click it, the default behaviour is to sort it in ascending order.
Is there any way I can change it so that the first click sorts in descending order?
It can be done, here is how.
First you have to override the default table.html template that is used for rendering table (if you are not already using your own template). You can start with the one included in django-tables2.
Secondly, in the template, find the anchor used for sorting:
{% if column.orderable %}
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% else %}
And change this to:
{% if column.orderable %}
{% with "-"|add:column.name as sort_col_name %}
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% endwith %}
{% else %}
So we are basically just adding a - in front of the column name by default in order to force descending sort in case when the page is loaded without sorting, and turning to .opposite instead of .next. We need to do this because .next will always be defined whereas .opposite will be None on the page load without sort, and the default filter will get executed. When sorting is defined, .opposite will properly toggle the sort order.
Note that this is not tested, and if you have problems take a look into the django-tables2 source for more info.