9

I have a single list my_list in my context, and I'd like to render it as two "columns", with the first (n+1)/2 items in the first column and the last n/2 items in the second column. Is there a straight-forward way to do this with django template tags/filters or do I need to pre-split the list into two in my view?

e.g.,

<div class="split-50-left">
  <ul> 
    {% for item in [first half of my_list] %}
      <li>{{item}}</li>
    {% endfor %}
  </ul>
</div>
<div class="split-50-right">
  <ul> 
    {% for item in [second half of my_list] %}
      <li>{{item}}</li>
    {% endfor %}
  </ul>
</div>
B Robster
  • 34,955
  • 14
  • 82
  • 116
  • [Here is a templatetag](http://djangosnippets.org/snippets/660/) to split into uniform chunks. Usage `{% split_list my_list as chunked_data 2 %}` – karthikr Jul 30 '13 at 15:42

4 Answers4

13

The more 'Django' way would be to do it in the view, as you are supposed to keep as much logic out of your template as possible. That being said, there is a ways you can do it through the template.

You can use the slice template tag if you already know how many are in the list. Let's assume you don't though.

The other method would be to just loop over it twice, and only display half you want. You would be going over the entire list though each time, so it might be expensive. It uses the forloop counter.

{% for item in items %}
#half list is calculated in your view. It is the items query /2
   {% if forloop.counter < half_list %}
       {% item.name %}
   {% endif %}
{% endfor %}

{% for item in items %}
#half list is calculated in your view. It is the items query /2
    {% if forloop.counter >= half_list %}
        {% item.name %}
    {% endif %}
{% endfor %}
JcKelley
  • 1,884
  • 1
  • 12
  • 29
  • I was about to say something similar (in regards to it being the more Django way to do it being in the view), but on the other hand, isn't that really display logic? Which Django says _should_ be done in the template. – Daniel Rosenthal Jul 30 '13 at 15:54
  • 1
    Good point. I've always tried to keep as much logic out of the template regardless if it is display or not. You're given far more power, the template is just so restricting. – JcKelley Jul 30 '13 at 16:22
  • 1
    It totally is, it'd be much easier to do it in the view. It's too bad, really, that the template language is so limiting. Sometimes display logic does require actual logic. – Daniel Rosenthal Jul 30 '13 at 16:31
  • As you mention, it does seem like "display" logic, but given the limitations of the template langauge, I went with splitting the list in the view and putting it in the context as "left_items" and "right_items", since i would at a minimum have to caclulate "half_list" or something along those lines in my view anyway. Thanks! `left_items = items[:(len(items)+1)/2]` `right_items = items[(len(items)+1)/2:]` – B Robster Jul 30 '13 at 17:56
  • Depending on what the markup looks like you don't have to call the forloop twice, you can just do `{% if forloop.counter == half_list %}` and then close the first list and open the second list inside of that. – Brendan Nov 01 '16 at 21:57
1

A (slightly hacky) way to do this entirely in the template is to use widthratio template tag to work out the centre of the list and the with template tag to create temporary variables.

{% widthratio form.visible_fields|length 2 1 as visible_fields_centre %}
<div class="rows_form">
    {% with ":"|add:visible_fields_centre as first_slice %}
        {% for field in form.visible_fields|slice:first_slice %}
            {{ field }}
        {% endfor %}
    {% endwith %}
</div>
<div class="rows_form">
    {% with visible_fields_centre|add:":" as second_slice %}
        {% for field in form.visible_fields|slice:second_slice %}
            {{ field }}
        {% endfor %}
    {% endwith %}
</div>
1

If you could interpolate the two halves -- or if splitting down the middle isn't critical -- you could render alternate values separately. This'll be cheaper:

<ul><!-- Half of the list -->
    {% for i in values %}
        {% cycle 'odd' 'even' as state silent %}
        {% if state == 'odd' %}
            <li>{{ i }}</li>
        {% endif %}
    {% endfor %}
</ul>

<ul><!-- The other half -->
    {% for i in values %}
        {% cycle 'odd' 'even' as state silent %}
        {% if state == 'even' %}
            <li>{{ i }}</li>
        {% endif %}
    {% endfor %}
</ul>
Aidan Fitzpatrick
  • 1,652
  • 1
  • 19
  • 24
1

Does it make sense at all to use css? I believe it would work everywhere except IE9.

<ul class="two-columns">
{% for item in object_list %}
  <li>{{ item }}</li>
{% endfor %}
</ul>

And then the css:

ul.two-columns {
    -moz-column-count: 2;
    -webkit-column-count: 2;
    column-count: 2;
}
putty
  • 567
  • 1
  • 5
  • 12