4

我正在尝试找出一个棘手的 Django 查询,希望您能提供帮助。我有这个模型:

class ActiveVenue(models.Model):
    event = models.ForeignKey(Event)
    venue = models.ForeignKey(Venue)

class Venue(models.Model):
    name = models.CharField(max_length=200)

class Event(models.Model):
    user = models.ForeignKey(User)
    name = models.CharField(max_length=200)

在我的应用程序中有很多事件,每个事件都可以有多个活动场所,因此是我当前的数据结构。我希望您的解决方案不是“将您的模型更改为 foo”,因为这已经是一个已部署的站点,我想保留当前的模型结构。

我想编写一个查询,返回最受欢迎的场所,但每个用户只计算一次场所。例如,如果我有一个用户参加四个活动,并且他们每次都使用同一个场地,那么在确定最受欢迎的场地时,我只想计算一次该场地。

为了说明这一点,假设这是我的数据:

event: A    user: bob   venue: The Hill
event: B    user: bob   venue: The Hill
event: C    user: bob   venue: The Hill
event: D    user: jane   venue: The Oaks
event: E    user: sarah   venue: The Pound
event: F    user: david   venue: The Pound
event: G    user: ron   venue: The Oaks
event: H    user: erica   venue: The Oaks

这里流行的顺序是:

1. The Oaks
2. The Pound
3. The Hill

有什么建议我如何编写查询来执行此操作,该查询适用于 postgres 和 sqlite(换句话说,不依赖distinct()(在 sqlite 中不受支持))?

谢谢!

4

4 回答 4

1

这行得通吗?

from collections import Counter 
results = Counter([vid for vid, eid in ActiveVenue.objects.values_list("venue_id", "event_id").distinct()]
于 2013-03-26T06:07:07.090 回答
0

这是我对这个问题的看法。首先,这是一个查询,它将获取与相关用户对应的 Venue id 和分数。

testquery = ActiveVenue.objects.values("venue").annotate(score=Count("event__user", distinct=True)).order_by("-score")

结果

[{'score': 3, 'venue': 2}, {'score': 2, 'venue': 3}, {'score': 1, 'venue': 1}]

接下来,场地 ID 将被放置在一个新列表中。

query_ids = [item["venue"] for item in testquery]
[2, 3, 1]

接下来,必须获得相应的 Venue 对象。

tempresult = Venue.objects.filter(id__in=query_ids)
[<Venue: The Hill>, <Venue: The Oaks>, <Venue: The Pound>

最后,将执行列表推导以根据之前获得的分数对 Venue 对象进行重新排序。

result = [venue for venue_id in query_ids for venue in tempresult if venue_id == venue.id]
[<Venue: The Oaks>, <Venue: The Pound>, <Venue: The Hill>]

这会根据测试数据给出正确的结果。

于 2013-04-01T20:44:48.580 回答
0

这是我的版本(完成测试):

模型.py

class Venue(models.Model):
    name = models.CharField(max_length=200)

    def __unicode__(self):
        return self.name

    def ranking(self):
        count=0
        actives = ActiveVenue.objects.filter(
            venue__name=self.name).values(
            'event__user__username', 'venue__name').distinct()
        for active in actives:
            count += 1
        return count

视图.py

def getRanking( anObject ):
    return anObject.ranking()

def myview(request):
    venues = list(Venue.objects.filter())
    venues.sort(key=getRanking, reverse=True)
    return render(request,'page.html',{'venues': venues})

模板

{% for venue in venues %}
    {{forloop.counter}}. {{venue}}<br/>
{% endfor %}

输出:

  1. 橡树
  2. 英镑
  3. 小山
于 2013-03-26T06:40:30.997 回答
0

看看这样的东西是否有效

ActiveVenue.objects.all().annotate(score=Count('event__user', distinct=True)).order_by('-score')
于 2013-03-26T06:30:32.210 回答