Django删除查询集中除了最后五个以外的所有项
我这里有一个超级简单的 Django 模型:
class Notification(models.Model):
message = models.TextField()
user = models.ForeignKey(User)
timestamp = models.DateTimeField(default=datetime.datetime.now)
我使用 Ajax 每分钟检查一次新消息。我只会在任何时候向用户显示最近的五条通知。我想避免的情况是这样的。
用户登录时没有任何通知。在用户的窗口打开期间,他收到了 10 条新消息。因为我只显示五条,所以没什么大不了的。问题出现在用户开始删除通知的时候。如果他删除了显示的五条通知,下一次 Ajax 调用或刷新时,之前的五条旧通知就会显示出来。
我希望在保存新通知时,我的模型的保存方法能删除除了最近五条以外的所有通知。不幸的是,不能用 [5:] 这样的方式来实现。谁能帮帮我?
编辑
我尝试了这个,但效果并不如预期(在模型的保存方法中):
notes = Notification.objects.filter(user=self.user)[:4]
Notification.objects.exclude(pk__in=notes).delete()
我找不到奇怪行为的规律,但经过一段时间的测试,只有在创建新通知时,它才会删除最近的一条。我完全不知道为什么会这样。排序是在模型的 Meta 类中处理的(按时间戳降序)。谢谢大家的帮助,但我觉得我的方法似乎是唯一一个能稳定工作的。
3 个回答
这是我最后做到这个的方式。
notes = Notification.objects.filter(user=self.user)
for note in notes[4:]:
note.delete()
因为我是在保存方法里做这个的,所以循环只会在用户同时收到多个通知时才会运行超过一次。我并不担心这种情况会发生(虽然有可能发生,但不太可能会造成问题)。
使用内部查询来获取你想保留的项目集合,然后再对这些项目进行筛选。
objects_to_keep = Notification.objects.filter(user=user).order_by('-created_at')[:5]
Notification.objects.exclude(pk__in=objects_to_keep).delete()
在使用之前,最好再检查一下。我发现一些简单的内部查询有时并不会按预期工作。我遇到的奇怪情况主要出现在那些只有排序和切片的查询集上。因为你需要根据用户进行筛选,所以你应该没问题。
这个内容有点旧,但我觉得你可以这样做:
notes = Notification.objects.filter(user=self.user)[:4]
Notification.objects.exclude(pk__in=list(notes)).delete() # list() forces a database hit.
这样做需要两次操作,但可以避免在事务中使用for循环。
之所以使用 list(notes)
是因为如果不这样做,Django会只生成一个查询,而在Mysql 5.1中,这会导致错误
(1235, "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'")
通过使用 list(notes)
,我们强制对 notes
进行查询,从而避免了这个问题。接下来可以进一步优化为:
notes = Notification.objects.filter(user=self.user)[:4].values_list("id", flat=True) # only retrieve ids.
Notification.objects.exclude(pk__in=list(notes)).delete()