grigy
grigy

Reputation: 6826

How to display a list of objects containing many-to-many relations in Django template?

I have the following models:

class Tag(models.Model):
  name = models.CharField(max_length=20)

class Entry(models.Model):
  title = models.CharField(max_length=100)
  date = models.DateField()
  tags = models.ManyToManyField(Tag)

In a view I create a list of Entry object and want to show the elements in the template:

   {% for entry in entries %}
     {{ entry.title }}
     {{ entry.date }}
   <!--  {% for tag in entry.tags %} {{ tag }} {% endfor %} -->
   {% endfor %}

And with this template code it generates the following TemplateSyntaxError pointing to the template's first line (for tag):

Caught TypeError while rendering: 'ManyRelatedManager' object is not iterable

The entries variable is a list:

entries = Entry.objects.filter(user=user_id)
entries = list(entries)
entries.sort(key=lambda x: x.id, reverse=False)

Do you know what can be the problem here and how to resolve this issue?

I'm new to Django, so any suggestions how to debug the templates may be helpful.

Update

I get the same error even with this template:

{% for entry in entries.all %}
<!-- everything is commented out here -->
{% endfor %}

Upvotes: 15

Views: 34113

Answers (4)

Anupam Shakya
Anupam Shakya

Reputation: 151

Here is the solution of your query,

Verifying your solution by giving an example

Suppose a book have number of tags, so in order to display all the tags of a book on template can be like this

{% for tag in book.tags.all %}
  {{ tag.name }}
{% endfor %}

where the model of Tag is like,

class Tag(models.Model):
    name = models.CharField(max_length=100)

    def __unicode__(self):
    return "%s" % unicode(self.name)

Upvotes: 11

grigy
grigy

Reputation: 6826

OK. I found the problem. I had some incorrect code which was commented out. But Django processed that code. So html comments didn't work here. I fixed this and it all worked like a charm.

So if you didn't know - the html comments don't prevent template processing.

This is because the template is being processed by Django first then HTML is rendered by browser.

Upvotes: 5

istruble
istruble

Reputation: 13702

There is no need to turn the entries QuerySet into a list. Additionally, you can let the DB do the sorting using order_by.

entries = Entry.objects.filter(user_id=user_id).order_by('id')

Add .all to get all the values from a relationship (just like Entry.objects.all()).

entry.tags.all

You can try this in the shell as well (I use ipython so your output may look different):

$ ./manage.py shell
# ...
In [1]: from yourproject.models import Entry, Tags
In [2]: entry = Entry.objects.all()[0]
In [3]: entry.tags
Out[3]: <django.db.models.fields.related.ManyRelatedManager object at 0x...>
In [4]: entry.tags.all()  # for an entry with no tags.
Out[4]: []
In [5]: # add a few tags
In [6]: for n in ('bodywork', 'happy', 'muscles'):
   ...:     t, created = Tag.objects.get_or_create(name=n)
   ...:     entry.tags.add(t)
In [7]: entry.tags.all()
Out[7]: [<Tag: ...>, <Tag: ...>, <Tag: ...>]

And if you want to call out the entries with zero tags use for..empty.

{% for tag in entry.tags.all %}
    {{ tag.name }}
{% empty %}
    No tags!
{% endfor %}

Upvotes: 44

sunn0
sunn0

Reputation: 3046

The above from istruble is correct but if your question contains all of your code you need to specify a property in your template:

   {% for entry in entries %}
     {{ entry.title }}
     {{ entry.date }}
     {% for tag in entry.tags.all %} {{ tag.name }} {% endfor %}
   {% endfor %}

or a default unicode function to your model:

class Tag(models.Model):
  name = models.CharField(max_length=20)
  def __unicode__(self):
      return self.name

Upvotes: 4

Related Questions