Search code examples
pythondjangomany-to-many

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


Solution

  • 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()