Hey, I have a model LeadsClicks
I create from this model every time someone goes to one of my website links
class LeadsClicks(models.Model):
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
business = models.ForeignKey(
Business,
on_delete=models.CASCADE,
related_name="leads_clicks",
verbose_name=_("business"),
)
qr = models.ForeignKey(
"BusinessQR",
on_delete=models.CASCADE,
related_name="leads_clicks",
null=True,
blank=True,
verbose_name=_("QR"),
)
referrer = models.CharField(_("referrer"), max_length=1000, blank=True, null=True)
I want to create an api and return results summed daily
"results": [
{
"day": "2025-06-22",
"clicks": 63,
"info": {
"qr__name1": 60,
"qr__name2": 3
}
},
{
"day": "2025-06-29",
"clicks": 51,
"info": {
"qr__name1": 46,
"qr__name2": 1,
"qr__name3": 2,
"qr__name4": 2
}
},
I manage to do it, but not 100% in the Django DRF way…
How could I improve it? Ordering is done manually, and everything feels wrong…
class DailyLeadsClicksAPIView(viewsets.ReadOnlyModelViewSet):
#serializer_class = LeadsClicksSerializer2
pagination_class = CustomPagination
filter_backends = [filters.OrderingFilter, DjangoFilterBackend]
filterset_class = LeadsClicksCustomFilter
ordering_fields = ['day', 'clicks']
ordering = ['-day']
def get_queryset(self):
biz = self.request.user.profile.biz
qs = LeadsClicks.objects.filter(business=biz, qr__isnull=False).annotate(
day=TruncDate('created_at')
).annotate(
clicks=Count('id', distinct=True)
).values('day', 'qr__name', 'clicks')
return qs
def list(self, request, *args, **kwargs):
# from queryset
# (day, qr__name, clicks)
# to resp:
# (day, clicks, info: {key: qr__name, value: clicks})
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
from collections import defaultdict
result = defaultdict(lambda: {"clicks": 0, "info": {}})
for row in queryset:
day_str = row['day'].isoformat()
result[day_str]["clicks"] += row['clicks']
if result[day_str]["info"].get(row['qr__name']):
result[day_str]["info"][row['qr__name']] += row['clicks']
else:
result[day_str]["info"][row['qr__name']] = row['clicks']
resp_data = [
{"day": day, "clicks": data["clicks"], "info": data["info"]}
for day, data in result.items()
]
# Sort by clicks manually
ordering = request.query_params.get('ordering')
if ordering == 'clicks':
resp_data.sort(key=lambda x: x['clicks'], reverse=False)
elif ordering == '-clicks':
resp_data.sort(key=lambda x: x['clicks'], reverse=True)
elif ordering == '-day':
resp_data.sort(key=lambda x: x['day'], reverse=True)
elif ordering == 'day':
resp_data.sort(key=lambda x: x['day'])
page_number = int(self.paginator.get_page_number(request, self.paginator)) - 1
page_size = int(self.paginator.get_page_size(request))
resp_data2 = resp_data[page_size*page_number: page_size*(page_number+1)]
resp = self.get_paginated_response(resp_data2)
resp.data['count'] = len(resp_data)
return resp
BTW: The info can have a different shape as appropriate. And I use PostgreSQL
I did not find a way to do it with a single query and without Python