问题
I have a Django-based web-app where users congregate and chat with one another. I've just finished writing a feature whereby users can make their own "chat groups", centered around any topic of interest. These could either be private, or publically visible.
My next challenge is showing a list of all existing public groups, paginated, and sorted by the most happening group first. After some deep thinking, I've decided that the most happening group is one which sees the most unique visitors (silent or otherwise) in the previous 60 mins. To be sure, by unique I mean distinct users, and not the same user hitting a group again and again.
What's the most efficient way to get the desired, ordered query-set in the get_queryset()
method of my class-based view associated to this popularity listing? Secondly, what's the most efficient way to also annotate total distinct views to each group object in the same queryset, so that I can additionally show total views, while sort according to what's currently hot?
Relevant models are:
class Group(models.Model):
topic = models.TextField(validators=[MaxLengthValidator(200)], null=True)
owner = models.ForeignKey(User)
private = models.CharField(max_length=50, default=0)
created_at = models.DateTimeField(auto_now_add=True)
class GroupTraffic(models.Model):
visitor = models.ForeignKey(User)
which_group = models.ForeignKey(Group)
time = models.DateTimeField(auto_now_add=True)
The relevant view is:
class GroupListView(ListView):
model = Group
form_class = GroupListForm
template_name = "group_list.html"
paginate_by = 25
def get_queryset(self):
return Group.objects.filter(private=0,date__gte=???).distinct('grouptraffic__visitor').annotate(recent_views=Count('grouptraffic__???')).order_by('-recent_views').annotate(total_views=Count('grouptraffic__which_group=group'))
As you can see, I've struggled rather mightily in the get_queryset(self)
method above, annotating twice and what not. Please advise!
回答1:
You cannot combine annotate()
and distinct()
in a single django query. So you can try like:
date = datetime.datetime.now()-datetime.timedelta(hours=1)
Next query is to get the grouptraffic with unique visitors
new_traff = GroupTraffic.objects.filter(time__gte=date).distinct('visitor','which_group').values_list('id',flat=True)
trendingGrp_ids = GroupTraffic.objects.filter(id__in=new_traff).values('which_group').annotate(total=Count('which_group')).order_by('-total')
The above query will get you trending groupids ordered by total
like:
[{'total': 4, 'which_group': 2}, {'total': 2, 'which_group': 1}, {'total': 1, 'which_group': 3}]
Here total
refers to no. of new unique visitors for each group in the last 60 minutes.
Now iterate over trendingGrp_ids to get the trending trendingGrps with views:
trendingGrps = [Group.objects.filter(id=grp['which_group']).extra(select={"views":grp['total']})[0] for grp in trendingGrp_ids]
Update:
To get all public groups, and sort them by how hot they are via measuring the traffic they received in the past 1 hr.
new_traff = GroupTraffic.objects.filter(time__gte=date,which_group__private=0).distinct('visitor','which_group').values_list('id',flat=True)
trendingGrp_ids = GroupTraffic.objects.filter(id__in=new_traff).values('which_group').annotate(total=Count('which_group')).order_by('-total')
trendingGrps = [Group.objects.filter(id=grp['which_group']).extra(select={"views":grp['total']})[0] for grp in trendingGrp_ids]
trndids = [grp['which_group'] for grp in trendingGrp_ids]
nonTrendingGrps = Group.objects.filter(private=0).exclude(id__in=trndids).extra(select={"views":0})
allGrps = trendingGrps.append(nonTrendingGrps)
回答2:
1)Create a separate function that tallies the distinct views in a chatbox. For every chatbox, put the result in a list. Return the biggest value in the list and assign it to a variable. Import the function and filter with the variable.
2) Make a set for each box. The set contains all the distinct users of the chatbox. Filter by the lenght of the set.
来源:https://stackoverflow.com/questions/33577671/highly-challenging-queryset-filtering-sorting-and-annotation-in-django-based-a