在Django/DRF中实现文章访问/评论筛选及访问状态显示问题
问题解决与功能实现方案
一、修复序列化相关错误
1. 解决seen_by_user显示绑定方法的问题
在Article模型中定义判断用户是否查看过的方法,确保返回布尔值而非方法对象:
from django.db import models from django.contrib.auth import get_user_model User = get_user_model() class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) def seen_by_user(self, user): if not user.is_authenticated: return False return ArticleSeenRecord.objects.filter(article=self, user=user).exists() class ArticleSeenRecord(models.Model): article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='seen_records') user = models.ForeignKey(User, on_delete=models.CASCADE) seen_at = models.DateTimeField(auto_now=True) class Meta: unique_together = ('article', 'user') # 避免重复记录
2. 修复SerializerMethodField的NameError问题
在序列化器中通过obj参数获取当前文章实例,同时传入请求上下文获取用户:
from rest_framework import serializers from .models import Article class ArticleSerializer(serializers.ModelSerializer): seen_by_user = serializers.SerializerMethodField() class Meta: model = Article fields = ['id', 'title', 'content', 'author', 'created_at', 'seen_by_user'] def get_seen_by_user(self, obj): # obj为当前Article实例,从上下文获取请求用户 user = self.context['request'].user return obj.seen_by_user(user)
视图中序列化时必须传入请求上下文:
serializer = ArticleSerializer(articles, many=True, context={'request': request})
二、实现模板标签判断用户是否查看过文章
- 在app目录下创建
templatetags文件夹,新建article_tags.py:
from django import template from ..models import ArticleSeenRecord register = template.Library() @register.filter(name='has_seen') def has_seen(article, user): if not user.is_authenticated: return False return ArticleSeenRecord.objects.filter(article=article, user=user).exists()
- 在模板中加载并使用标签:
{% load article_tags %} {% for article in articles %} <div class="article-item"> <h3>{{ article.title }}</h3> {% if user|has_seen:article %} <span class="tag">已查看</span> {% else %} <span class="tag">未查看</span> {% endif %} </div> {% endfor %}
三、实现筛选最近访问/评论过的文章功能
1. 筛选最近访问的文章
def get_recent_seen_articles(user, limit=10): if not user.is_authenticated: return Article.objects.none() # 获取用户最近访问记录,按访问时间倒序 seen_records = ArticleSeenRecord.objects.filter(user=user).order_by('-seen_at')[:limit] article_ids = [record.article.id for record in seen_records] # 保持访问顺序返回文章 return Article.objects.filter(id__in=article_ids).order_by( models.Case(*[models.When(id=aid, then=pos) for pos, aid in enumerate(article_ids)]) )
2. 筛选最近评论的文章
假设已有关联Article和User的Comment模型:
def get_recent_commented_articles(user, limit=10): if not user.is_authenticated: return Article.objects.none() # 获取用户最近评论记录,去重文章后按评论时间倒序 comments = Comment.objects.filter(user=user).order_by('-created_at').distinct('article')[:limit] article_ids = [comment.article.id for comment in comments] return Article.objects.filter(id__in=article_ids).order_by( models.Case(*[models.When(id=aid, then=pos) for pos, aid in enumerate(article_ids)]) )
3. 合并筛选访问/评论过的文章(去重并按最近操作时间排序)
from django.db.models import Value, Case, When, F def get_recent_interacted_articles(user, limit=10): if not user.is_authenticated: return Article.objects.none() articles = Article.objects.annotate( last_interacted_at=Case( When(seen_records__user=user, then=F('seen_records__seen_at')), When(comments__user=user, then=F('comments__created_at')), default=Value('1970-01-01'), output_field=models.DateTimeField() ) ).filter( models.Q(seen_records__user=user) | models.Q(comments__user=user) ).distinct().order_by('-last_interacted_at')[:limit] return articles
内容的提问来源于stack exchange,提问作者Abdullah Roshan
相关产品推荐
相关产品推荐

