Django获取48小时内数据的简便实现方法咨询
Hey there! Great question—let's simplify that code and get you the 48-hour hot news list you need without all the loop hassle.
First, let's break down what your original code does: it loops over the last two days, queries daily read counts, and aggregates them. But if your goal is to get the most viewed news articles in the last 48 hours (not just daily totals), we can streamline this with Django's ORM tools like annotate, Sum, and proper time filtering.
Case 1: Your News model has a direct pv field
If you're updating a pv field directly on the News model (like in your example code), this is super straightforward:
from django.utils import timezone from django.db.models import F from django.shortcuts import get_object_or_404, render def newsDetailView(request, news_pk): # Get the news article or return 404 news = get_object_or_404(News, id=news_pk) # Update PV atomically (avoids race conditions from concurrent requests) News.objects.filter(id=news_pk).update(pv=F('pv') + 1) # Calculate the cutoff time for 48 hours ago forty_eight_hours_ago = timezone.now() - timezone.timedelta(hours=48) # Filter news published/updated in the last 48 hours, ordered by PV descending # Replace `publish_time` with your actual time field (e.g., `update_time` if tracking recent views) forty_eight_hours_hot_news = News.objects.filter( publish_time__gte=forty_eight_hours_ago ).order_by('-pv') return render(request, "news_detail.html", { 'news': news, 'forty_eight_hours_hot_news': forty_eight_hours_hot_news })
Case 2: You're using a ReadDetail model to track views
If you're relying on a separate ReadDetail model (like your original code), we can aggregate the total PV for each news article in one query instead of looping through days:
from django.utils import timezone from django.db.models import Sum from django.contrib.contenttypes.models import ContentType from django.shortcuts import get_object_or_404, render def newsDetailView(request, news_pk): news = get_object_or_404(News, id=news_pk) # Add your existing code to create/update the ReadDetail record here # ... # Get the content type for the News model news_content_type = ContentType.objects.get_for_model(News) # Calculate 48-hour cutoff forty_eight_hours_ago = timezone.now() - timezone.timedelta(hours=48) # Aggregate total PV for each news article in the last 48 hours forty_eight_hours_hot_news = News.objects.filter( # Filter related ReadDetail records within the time frame readdetail__content_type=news_content_type, readdetail__date__gte=forty_eight_hours_ago.date() # Use `datetime__gte` if your date field is a datetime type ).annotate( total_pv=Sum('readdetail__read_num') ).order_by('-total_pv') return render(request, "news_detail.html", { 'news': news, 'forty_eight_hours_hot_news': forty_eight_hours_hot_news })
Bonus: Simplifying your original get_two_days_read_data function
If you still need daily read count totals (e.g., for a chart), you can use Django's TruncDate to group results by day without looping:
from django.utils import timezone from django.db.models import Sum from django.db.models.functions import TruncDate def get_two_days_read_data(content_type): forty_eight_hours_ago = timezone.now() - timezone.timedelta(hours=48) # Group read details by date and sum the read counts daily_read_data = ReadDetail.objects.filter( content_type=content_type, date__gte=forty_eight_hours_ago.date() ).annotate( day=TruncDate('date') ).values('day').annotate( total_read=Sum('read_num') ).order_by('day') # Extract dates and read numbers in your desired format dates = [entry['day'].strftime('%m/%d') for entry in daily_read_data] read_nums = [entry['total_read'] or 0 for entry in daily_read_data] return dates, read_nums
Key improvements here:
- Single queries instead of loops: Reduces database hits and improves performance.
- Timezone-aware filtering: Uses
timezone.now()to avoid issues with time zone mismatches. - Atomic updates: Using
F('pv') + 1ensures PV counts stay accurate even with multiple concurrent requests.
内容的提问来源于stack exchange,提问作者William

