Home > Software design >  zipped data shown in view but not in templete
zipped data shown in view but not in templete

Time:06-26

row1 = [] row2 = []

categories= Kategoriler.objects.all().filter(parent_id__isnull=True).order_by('id')
for x in categories:
    row1.append(x.title)
    row2.append(Kategoriler.objects.all().filter(parent_id=x.id).order_by('title'))

zipped = itertools.zip_longest(row1, row2)


for u, y in zipped:
    print(">>>>>>>>>>>>>>>", u, "<<<<<<<<<<<<<<<<<<<<")
    for q in y:
        print(q.title)


context = {'segment': 'categories', "zipped": zipped}

Above code prints as expected in view.py

In templete;

{{ zipped|length }}
                {%  for u, y in zipped %}
                    {{ u }}---{{ y.title }}
                {%  endfor %}

len gives 0, and loop is empty. What's the reason of it?

CodePudding user response:

You should not enumerate over zip in the view, since that will "consume" the iterator, and then the iterator is thus "exhausted". You pass zipped to the view without enumerating, so:

from itertools import zip_longest

row1 = []
row2 = []
categories = Kategoriler.objects.filter(parent=None).order_by('id')
for x in categories:
    row1.append(x.title)
    row2.append(Kategoriler.objects.filter(parent_id=x.id).order_by('title')

zipped = zip_longest(row1, row2)
# no enumeration
context = {'segment': 'categories', "zipped": zipped}

In the template you can then render with:

{% for u, ys in zipped %}
    {% for y in ys %}
        {{ u }}---{{ y.title }}
    {% endfor %}
{% endfor %}

You should not use {{ zipped|length }} since this will again consume the zipper.

That being said, the zipping is unnecessary and inefficient: it will make N 1 queries if categories contains N elements. You can fetch the items with:

from django.db.models import Prefetch

categories = Kategoriler.objects.filter(
    parent=None
).prefetch_related(
    Prefetch('kategoriler_set', Kategoriler.objects.order_by('title'))
).order_by('id')
context = {'segment': categories}

In the template you can then render this with:

{% for segment in segment %}
    {% for sub in segment.kategoriler_set.all %}
        {{ segment.title }}---{{ sub.title }}
    {% endfor %}
{% endfor %}

If you set a related_name=… [Django-doc] for the ForeignKey named parent, then you should replace kategoriler_set with that value.

  • Related