Vincent de Jong Vincent de Jong - 28 days ago 16
Python Question

How do you filter on many-to-many relationships using classes (Python/Django)

Im trying to filter posts belonging to a certain theme. I have a many-to-many relationship as you can see in my models. The problem is that I don't know how to filter. Normally I would do that by ID, but that didnt work.

Models:

class Theme(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(_('slug'), max_length=255, null=True, blank=True)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
image = FilerImageField()

def publish(self):
self.save()

def __unicode__(self):
return self.title

class Post(models.Model):
writer = models.ForeignKey(Author, blank=True, null=True)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
themes = models.ManyToManyField(Theme)

def publish(self):
self.published_date = timezone.now()
self.save()

def __unicode__(self):
return self.title


Views:

from .models import Theme, Post
from django.views.generic import ListView, DetailView


class ThemesOverview(ListView):
"""
Overview of all themes
"""
model = Theme
template_name = 'content/theme_list.html'

def get_queryset(self):
queryset = Theme.objects.all()
return queryset


class ThemePostsOverview(ListView):
"""
Overview of all posts within a theme
"""
model = Post
template_name = 'content/theme_posts_list.html'

def get_context_data(self, **kwargs):

# Call the base implementation first to get a context
context = super(ThemePostsOverview, self).get_context_data(**kwargs)

slug = self.kwargs['theme']
theme = Theme.objects.get(title=slug)
context['theme'] = theme

return context

def get_queryset(self):
queryset = Post.objects.all()
return queryset


As you can see I'm currently showing all posts instead of only the posts that belong to the theme

Answer

As we said in the comments, you need to filter the queryset by using the reverse relation on the theme. Here's one way of doing that:

class ThemePostsOverview(ListView):
    model = Post
    template_name = 'content/theme_posts_list.html'

    def get_context_data(self, **kwargs):
        context = super(ThemePostsOverview, self).get_context_data(**kwargs)
        context['theme'] = self.theme
        return context

    def get_queryset(self):
        slug = self.kwargs['theme']
        self.theme = Theme.objects.get(title=slug)
        return self.theme.post_set.all()