Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 50 additions & 8 deletions web/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,14 +286,56 @@ def index(request):
:10
] # Get more and then sort by clicks

# Add click counts manually since WebRequest.user is a CharField, not a ForeignKey
for referrer in top_referrers:
# Look for both new format /ref/CODE/ and old format ?ref=CODE
ref_code = referrer.referral_code
clicks = WebRequest.objects.filter(
models.Q(path__contains=f"/ref/{ref_code}/") | models.Q(path__contains=f"?ref={ref_code}")
).count()
referrer.total_clicks = clicks
# PERF FIX: Batch-fetch all click counts in a single query instead of N separate
# queries (one per referrer). This avoids an N+1 query problem that runs
# unindexed LIKE scans against a potentially huge WebRequest table on every
# homepage load.
if top_referrers:
all_codes = sorted([r.referral_code for r in top_referrers if r.referral_code], key=len, reverse=True)

if all_codes:
# Build a single combined Q expression for all codes at once
combined_q = models.Q()
for code in all_codes:
# Broadened contains check to catch /ref/CODE with or without trailing slash
combined_q |= models.Q(path__contains=f"/ref/{code}") | models.Q(path__contains=f"ref={code}")

# Fetch unique matching paths and their aggregate counts in one go
# Using Sum("count") to support models where rows represent multiple hits
matching_requests = (
WebRequest.objects.filter(combined_q)
.values("path")
.annotate(total_hits=models.Sum("count"))
)

from urllib.parse import parse_qs, urlparse

# Map each referral code to its click count in-memory
click_counts = {code: 0 for code in all_codes}
for req in matching_requests:
path = req["path"]
total_hits = req["total_hits"] or 0
extracted_code = None

# Try new format /en/ref/CODE/ or /en/ref/CODE
if "/ref/" in path:
# Improved regex to handle trailing slashes, end of path, query params, or fragments
m = re.search(r"/ref/([^/?#]+)(?:/|$|\?|#)", path)
if m:
extracted_code = m.group(1)

# Try query param format ?ref=CODE
if not extracted_code and "ref=" in path:
parsed_url = urlparse(path)
params = parse_qs(parsed_url.query)
if "ref" in params:
extracted_code = params["ref"][0]

if extracted_code and extracted_code in click_counts:
click_counts[extracted_code] += total_hits

for referrer in top_referrers:
referrer.total_clicks = click_counts.get(referrer.referral_code, 0)

# Re-sort to include click count in ranking
top_referrers = sorted(
Expand Down