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?
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