开发者

How to make a filtering annotation with ManyToMany relation in Django?

I have a model with articles, events and people:

class Person(models.Model):
  id = models.IntegerField(primary_key=True)
  name = models.CharField(max_length=30)

class Event(models.Model):
  id = models.IntegerField(primary_key=True)
  title = models.CharField(max_length=200)

class Article(models.Model):
  id = models.IntegerField(primary_key=True)
  title = models.CharField(max_length=200)
  publishDate = models.DateTimeField(blank=True, null=True)
  event = models.ForeignKey(Event, blank=False, null=False)
  persons = models.ManyToManyField(Person)

An article belongs to an event, and events consist of many articles. Persons appear in many articles and articles contain many persons. The idea is to see which events were most written about inside a given time interval. I did this in a single query:

topEvents = Article.objects.filter(publishDate__gt=dateStart)
                           .filter(publishDate__lt=dateEnd)
                           .values('event').annotate(count=Count('id'))
                           .order_by('-count')[:topN]

I found that 开发者_开发知识库this takes significantly less time than calculating the top N server-side.

Now, the question is, how do I do the same with the ManyToMany relation I have with the Persons? Also, is this the best way to do this?


To simplify your example:

class Event(models.Model):
    title = models.CharField(max_length=200)

class Person(models.Model):
    name = models.CharField(max_length=30)

class Article(models.Model):
    title = models.CharField(max_length=200)
    publishDate = models.DateTimeField(blank=True, null=True)
    event = models.ForeignKey(Event, related_name='articles')
    persons = models.ManyToManyField(Person, related_name='articles')

Note that there are no id fields as Django creates them automatically. Also, both event and persons on the Article class have related_name set to 'articles' which allows us to refer to articles simply by using event.articles and person.articles.

Now, to get the events and people that are written about most often, you can query directly on their model managers:

Event.objects.filter(articles__publishDate__gt=dateStart)\
             .filter(articles__publishDate__lt=dateEnd)\
             .annotate(count=Count('articles')).order_by('-count')

Person.objects.filter(articles__publishDate__gt=dateStart)\
              .filter(articles__publishDate__lt=dateEnd)\
              .annotate(count=Count('articles')).order_by('-count')


Have you tried:

topUsers = Article.objects.filter(publishDate__gt=dateStart)
                          .filter(publishDate__lt=dateEnd)
                          .values('persons').annotate(count=Count('id'))
                          .order_by('-count')[:topN]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜