如何在Django中构建模型以追踪用户文章阅读状态?
解决方案:通过中间模型追踪阅读状态
这个需求很常见,核心是得记录用户和文章之间的阅读关联关系——毕竟现有模型里完全没有字段能追踪“谁读了哪篇文章”。咱们可以通过新增一个中间模型来存储阅读记录,具体方案如下:
1. 修改/新增模型
首先加一个ArticleReadRecord模型,用来记录用户阅读文章的时间(用时间比单纯存“已读状态”更灵活,还能知道用户什么时候读的);同时调整Article模型,通过ManyToManyField关联User,并指定用这个中间模型来存储关联数据:
from django.db import models from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager class UserManager(BaseUserManager): # 这里默认你已经实现了create_user、create_superuser等必要方法 pass class User(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) name = models.CharField(max_length=20, blank=True) is_staff = models.BooleanField(default=False) is_active = models.BooleanField(default=True) date_joined = models.DateTimeField(auto_now_add=True) objects = UserManager() # 新增的阅读记录中间模型 class ArticleReadRecord(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='read_records') article = models.ForeignKey('Article', on_delete=models.CASCADE, related_name='read_records') read_at = models.DateTimeField(auto_now_add=True) # 有这条记录就代表用户已读 class Meta: unique_together = ('user', 'article') # 确保一个用户对同一篇文章只会有一条阅读记录 class Article(models.Model): # 注意类名首字母大写,符合Python命名规范 title = models.CharField(max_length=255, blank=False) content = models.TextField(max_length=1000, blank=False) created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='created_articles') created_at = models.DateTimeField(auto_now_add=True) # 新增多对多关联,通过中间模型记录阅读状态 read_by = models.ManyToManyField(User, through=ArticleReadRecord, related_name='read_articles')
2. 实现需求查询
需求1:获取某个用户的未读文章列表
思路就是:查所有文章,排除掉该用户已经读过的那些。可以用exclude快速过滤:
# 假设当前用户是request.user current_user = request.user # 获取未读文章 unread_articles = Article.objects.exclude(read_by=current_user)
如果担心关联字段的空值问题,也可以用更严谨的Q查询写法:
from django.db.models import Q unread_articles = Article.objects.filter( ~Q(read_records__user=current_user) )
需求2:查询某篇文章的已读/未读用户列表
- 已读用户列表:直接通过文章的
read_by字段获取就行:
target_article = Article.objects.get(id=article_id) read_users = target_article.read_by.all()
- 未读用户列表:查所有用户,排除掉已读这篇文章的用户:
unread_users = User.objects.exclude(read_articles=target_article) # 或者用Q查询写法 unread_users = User.objects.filter( ~Q(read_records__article=target_article) )
补充小提示
- 当用户完成阅读动作时,你只需要执行
target_article.read_by.add(current_user),Django会自动帮你在中间表创建一条阅读记录; unique_together能避免同一个用户重复记录同一篇文章的阅读状态,减少冗余数据;- 把原类名
article改成Article是遵循Python的大驼峰命名规范,建议保持代码风格统一。
内容的提问来源于stack exchange,提问作者chea
相关产品推荐
相关产品推荐

