简体   繁体   中英

How do I iterate through a ManyToManyField in django-jinja project?

I am trying to create a blog app which has posts and each posts have title, date, link and tags.

This is my models.py

# models.py
from django.db import models

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

    def __str__(self):
        return self.name

    class Meta:
        ordering = ('name',)

class Post(models.Model):
    title = models.CharField(max_length=300)
    date = models.DateTimeField()
    link = models.URLField()
    tags = models.ManyToManyField(Tag)

    def __str__(self):
        return self.title

    @property
    def tags_name(self):
        return [x.name for x in self.tags]

    class Meta:
        ordering = ('date',)

This is my views.py

# views.py
from django.conf.urls import url, include
from django.views.generic import ListView
from blog.models import Post

urlpatterns = [
    url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date"), template_name="blog/blog_list.html")),
]

This is my blog_list.html

<!-- blog_list.html -->
{% extends "mysite/layout.html" %}

{% block content %}
  <h1>my blog posts</h1>
  <ul>
    {% for post in object_list %}
      <li><span class="title"><a href="{{ post.link }}">{{ post.title }}</a></span></li>
      <p>{{ post.date|date:"d-m-Y" }}</p>
    {% endfor %}
  </ul>
{% endblock %}

{% block sidebar %}
  <h4 id="sidenav">tags</h4>
  {% for post in object_list %}
    <ul>
      <!-- I want to show the tags here -->
    </ul>
  {% endfor %}
{% endblock %}

In the blog_list.html, I am showing all the post details and on the sidebar, I want to show all the tags present from all the blog posts available. Since post.tags is ManyToManyField, how can I iterate through it?

You want to use .all in the template to get all the elements in the relationship:

{% for tag in post.tags.all %}
    {{ tag }}
{% endfor %}

Thanks to @hansTheFranz for correcting my bracket issue.

Regarding not repeating tags, this would be very difficult with the current context. You might want to look into instead getting the posts in your View and extracting the tags there, where you have more freedom to check for duplicates. Something like this:

def tags(request):
    posts = Post.objects.all()
    tag_list = []
    for post in posts:
        tags = post.tags.all()
        for tag in tag:
            if not (tag in tag_list):
                tag_list.append(tag)
    context_dict = { "tags": tag_list, "posts": posts }
    return render(request, 'blog/blog_list.html', context_dict)

urlpatterns = [
    url(r'^$', tags, name="tags"),
]

And then change your template to be more like:

{% block sidebar %}
  <h4 id="sidenav">tags</h4>
    <ul>
      {% for tag in tags %}
        <li>{{ tag }}</li>
      {% endfor %}
    </ul>
{% endblock %}

Additionally, instead of referencing object_list you can now access the list of posts by referencing posts , because we have defined the list of posts as such in our context dictionary, which is being passed to the template.

I'm afraid I have not tested this and it may not be very efficient, but roughly speaking it should work. A lecturer at my university wrote this book: http://www.tangowithdjango.com/book17/ , which encourages more of a style of writing views as I have done: separate from the URLs. If anything I've done seems unclear or contrary, you may want to have a look at the book and see if anything there makes more sense.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM