I recently made a tag cloud in my Wagtail-powered website, but I'm running into a problem where some tags (called "Topics" in my site) with only private articles/pages in them appear.
So far, I did something like this to get the number of articles in each tag and filter them out if there are none:
topics_with_articles = Topic.objects.annotate(num_articles=models.Count("articlepage")).filter(num_articles__gt=0)
filtered_topics = topics_with_articles.order_by("-num_articles").values("name", "slug", "num_articles")
And I have my models set up like this:
from modelcluster.models import ParentalManyToManyField, ParentalKey
from modelcluster.tags import ClusterTaggableManager
from taggit.models import Tag, TaggedItemBase
@register_snippet
class Topic(Tag):
class Meta:
ordering = ["slug"]
proxy = True
verbose_name = "Topic"
verbose_name_plural = "Topics"
class ArticleTopic(TaggedItemBase):
content_object = ParentalKey("ArticlePage", related_name="article_topics")
class ArticlePage(Page):
topics = ClusterTaggableManager(through="articles.ArticleTopic", blank=True)
I couldn't find an easy solution to get this to work, so how can I do that?
Someone in the Wagtail Slack chat suggested this answer on taggit's undocumented most_common
function, so I applied that solution something like this:
articles = ArticlePage.objects.live().public().order_by("-date")
topics_with_filtered_articles = ArticlePage.topics.most_common(min_count=1, extra_filters={'articlepage__in': articles})
This filters out all private and draft articles out of the topics by comparing between the articles
and Articles.objects
queryset. By adding the min_count=1
parameter, all topics without an article are also filtered out.
And as an added bonus, taggit made a num_times
annotation in the topics_with_filtered_articles
queryset so I don't have to do it myself!