Search code examples
djangodjango-modelsmodels

Django: how to filter a model with ManyToMany relationships


I have a generic ListView where I get some generic stuff (it's not related to my question).

I have model Product that has a ManyToMany relationship with Tag i.e. a Product can have many Tag and a Tag can be linked to many Product.

In this generic ListView, I want to filter all the Tag that have actually a Product so the customer could click on a Tag and I could filter later on.

So far I came to this:

class IndexView(generic.ListView):
    template_name = 'produits/index.html'
    context_object_name = 'liste_produits'

    def get_queryset(self):
        """Return the last five created products."""
        return Produit.objects.order_by('-date_v_fin', '-date_v_debut')[:5]

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)
        context['produits_tags'] = list(
            Tag.objects.values_list('nom', flat=True)
        )
        context['produits_tags'].insert(0, _("Tous"))
        return context

But the Tag.objects.values_list('nom', flat=True) returns all the Tag, including those who have no Product. How to filter this?


Solution

  • Found it! First of all, make the ManyToMany relationship more readable the other way round by using related_name like this:

    class Produit(BaseModel):
        tags = models.ManyToManyField(Tag, related_name='produits')
    

    Then here's how I get all the Tag that are used by Produit. I guess it's not very optimized, but it works like a charm:

    class IndexView(generic.ListView):
        #blabbla ignoring code in-between
        def get_context_data(self, **kwargs):
            context = super(IndexView, self).get_context_data(**kwargs)
            context['produits_tags'] = list(
                Tag.objects.filter(produits__in=Produit.objects.all()).distinct()
            )
            context['produits_tags'].insert(0, _("All"))
            return context