如何在Django中实现基于用户博客行为的个性化博客推荐功能?
Been there, done that — django-recommends docs are notoriously sparse, so I feel your pain. Let’s walk through a practical, self-implemented solution that’s easy to tweak and maintain for your blog recommendation feature.
We’ll break this into 5 actionable steps, starting with tracking user behavior, then building the recommendation logic, and finally displaying the results.
1. Model Setup for User Behavior Tracking
First, we need to define models to capture the user actions you mentioned: search queries, blog reads, and category follows. Assuming you already have Blog and Category models set up (with a foreign key or many-to-many link between them), add these to your app’s models.py:
from django.db import models from django.contrib.auth.models import User from .models import Blog, Category # Import your existing core models class UserSearch(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="search_history") query = models.CharField(max_length=255) timestamp = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["-timestamp"] class UserBlogRead(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="read_blogs") blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name="read_by_users") read_count = models.PositiveIntegerField(default=1) last_read = models.DateTimeField(auto_now=True) class Meta: unique_together = ["user", "blog"] # Prevent duplicate entries for the same user-blog pair class UserCategoryFollow(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="followed_categories") category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="followed_by_users") class Meta: unique_together = ["user", "category"]
Run migrations to apply these changes:
python manage.py makemigrations python manage.py migrate
2. Collecting User Behavior Data
Now hook these models into your existing views to capture user actions automatically.
Tracking Search Queries
In your search view, save the query whenever an authenticated user searches:
from django.shortcuts import render from .models import Blog, UserSearch def search_blogs(request): query = request.GET.get("q", "") if query and request.user.is_authenticated: # Save the search term for logged-in users UserSearch.objects.create(user=request.user, query=query) # Your existing search logic to fetch matching blogs blogs = Blog.objects.filter(title__icontains=query) | Blog.objects.filter(content__icontains=query) return render(request, "search_results.html", {"blogs": blogs, "query": query})
Tracking Blog Reads
In your blog detail view, update or create a read record when a user views a blog:
from django.shortcuts import get_object_or_404 from .models import Blog, UserBlogRead def blog_detail(request, blog_id): blog = get_object_or_404(Blog, id=blog_id) if request.user.is_authenticated: # Update read count if user has viewed this blog before, else create a new entry read_entry, created = UserBlogRead.objects.get_or_create(user=request.user, blog=blog) if not created: read_entry.read_count += 1 read_entry.save() return render(request, "blog_detail.html", {"blog": blog})
Tracking Category Follows
Add a view to handle category follow/unfollow actions (link this to a button in your template):
from django.http import JsonResponse from .models import Category, UserCategoryFollow def toggle_category_follow(request, category_id): if not request.user.is_authenticated: return JsonResponse({"status": "error", "message": "Login required"}, status=401) category = get_object_or_404(Category, id=category_id) follow_entry, created = UserCategoryFollow.objects.get_or_create(user=request.user, category=category) if not created: follow_entry.delete() return JsonResponse({"status": "success", "action": "unfollowed"}) else: return JsonResponse({"status": "success", "action": "followed"})
3. Building the Recommendation Logic
We’ll start with a simple, content-based recommendation system (effective for blogs and easy to iterate on). This prioritizes blogs from categories the user follows, plus similar blogs to those they’ve read.
Add this function to your app’s utils.py (or directly in your views if you prefer):
from django.db.models import Q, Count from .models import Blog, UserCategoryFollow, UserBlogRead def get_recommended_blogs(user, limit=5): if not user.is_authenticated: # For anonymous users, return popular blogs (most read) return Blog.objects.annotate(read_count=Count("read_by_users")).order_by("-read_count")[:limit] # Get categories the user follows followed_category_ids = user.followed_categories.values_list("id", flat=True) # Get blogs the user has already read (to avoid duplicate recommendations) read_blog_ids = user.read_blogs.values_list("blog__id", flat=True) # First, recommend blogs from followed categories (exclude read ones) recommended = Blog.objects.filter( category__id__in=followed_category_ids ).exclude(id__in=read_blog_ids) # If we don't have enough recommendations, add blogs similar to read ones (same category) if recommended.count() < limit: read_blog_category_ids = user.read_blogs.values_list("blog__category__id", flat=True) similar_blogs = Blog.objects.filter( category__id__in=read_blog_category_ids ).exclude(id__in=read_blog_ids).exclude(id__in=recommended.values_list("id", flat=True)) recommended = recommended | similar_blogs # If still not enough, add popular blogs as a fallback if recommended.count() < limit: popular_blogs = Blog.objects.annotate(read_count=Count("read_by_users")).order_by("-read_count").exclude(id__in=read_blog_ids).exclude(id__in=recommended.values_list("id", flat=True)) recommended = recommended | popular_blogs # Return distinct top N recommendations return recommended.distinct()[:limit]
4. Displaying Recommendations on the Homepage
In your homepage view, call the recommendation function and pass results to the template:
from django.shortcuts import render from .utils import get_recommended_blogs def homepage(request): recommended_blogs = get_recommended_blogs(request.user) # Your existing homepage logic (e.g., latest blogs) latest_blogs = Blog.objects.order_by("-published_date")[:10] return render(request, "homepage.html", { "latest_blogs": latest_blogs, "recommended_blogs": recommended_blogs })
Then render the "推荐博客" section in your homepage.html template:
<div class="recommended-blogs"> <h2>推荐博客</h2> {% if recommended_blogs %} <div class="blog-list"> {% for blog in recommended_blogs %} <div class="blog-card"> <h3><a href="{% url 'blog_detail' blog.id %}">{{ blog.title }}</a></h3> <p>{{ blog.excerpt|truncatechars:100 }}</p> <span class="category-tag">{{ blog.category.name }}</span> </div> {% endfor %} </div> {% else %} <p>暂无推荐内容,快去探索更多博客吧!</p> {% endif %} </div>
5. Optimization Tips
- Cache Recommendations: For authenticated users, cache results for 1-2 hours to reduce database load. Example:
from django.core.cache import cache def get_recommended_blogs(user, limit=5): cache_key = f"recommendations_{user.id}" cached_recs = cache.get(cache_key) if cached_recs: return cached_recs # Existing recommendation logic... cache.set(cache_key, recommended, 60*60*2) # Cache for 2 hours return recommended - Background Tasks: For high-traffic sites, use tools like Celery to update read counts asynchronously instead of on every request.
- Advanced Algorithms: Once you have enough user data, expand to collaborative filtering (e.g., "Users who read this also read...") using simple count-based logic, or integrate lightweight ML libraries like scikit-learn for more complex models.
内容的提问来源于stack exchange,提问作者Mr. Nobody

