Преобразование необработанного sql self join в код Django orm (без внутреннего внешнего ключа)
У меня есть модели Article
и Tag
, имеющие отношение многие ко многим через другую модель ArticleTag
. Я хочу найти для данного тега "Здоровье", сколько раз он встречался одновременно с другими тегами в статьях. (например, на 4 статьи с тегом "Ковид", на 0 статей с тегом "Инфекция" и т.д.)
Я могу выполнить самоприсоединение на ArticleTag
с некоторыми условиями Where и пунктом Group By и получить желаемый результат:
SELECT tag.title, COUNT(*) as co_occurrences
FROM app_articletag as t0
INNER JOIN app_articletag t1 on (t0.article_id = t1.article_id)
INNER JOIN app_tag tag on (tag.id = t1.tag_id)
WHERE t0.tag_id = 43 and t1.tag_id != 43
GROUP BY t1.tag_id, tag.title
Однако я хочу держаться как можно дальше от необработанных запросов и работать с API Django QuerySet.
Я видел другие темы о self join, но все их модели имеют внешний ключ к самому себе.
Вот мои модели Django:
class Tag(Model):
...
class Article(Model):
tags = models.ManyToManyField(Tag, through=ArticleTag, through_fields=('article', 'tag'))
class ArticleTag(Model):
tag = models.ForeignKey(Tag, on_delete=models.CASCADE))
article = models.ForeignKey(Article, on_delete=models.CASCADE))
Один из подходов заключается в следующем, учитывая, что t
является тегом Health
:
ArticleTag.objects.values(
"tag__name"
).annotate(
articles_with_health=Count(
"pk", filter=Q(article__articletag__tag=t)
)
).exclude(tag=t)
Это должно вернуть результат типа:
[
{'tag__name': 'Infection', 'articles_with_health': 0},
{'tag__name': 'Covid', 'articles_with_health': 4}
]