python Django删除查询集的最后五个以外的所有内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1851197/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 23:12:40  来源:igfitidea点击:

Django Delete all but last five of queryset

pythondjango

提问by Brandon Henry

I have a super simple django model here:

我这里有一个超级简单的 Django 模型:

class Notification(models.Model):
    message = models.TextField()
    user = models.ForeignKey(User)
    timestamp = models.DateTimeField(default=datetime.datetime.now)

Using ajax, I check for new messages every minute. I only show the five most recent notifications to the user at any time. What I'm trying to avoid, is the following scenario.

使用 ajax,我每分钟检查一次新消息。我在任何时候都只向用户显示五个最近的通知。我试图避免的是以下情况。

User logs in and has no notifications. While the user's window is up, he receives 10 new messages. Since I'm only showing him five, no big deal. The problem happens when the user starts to delete his notifications. If he deletes the five that are displayed, the five older ones will be displayed on the next ajax call or refresh.

用户登录并且没有通知。当用户的窗口打开时,他会收到 10 条新消息。因为我只给他看五个,没什么大不了的。当用户开始删除他的通知时会出现问题。如果他删除显示的五个,则在下一次 ajax 调用或刷新时将显示五个较旧的。

I'd like to have my model's save method delete everything but the 5 most recent objects whenever a new one is saved. Unfortunately, you can't use [5:] to do this. Help?

我想让我的模型的 save 方法在保存一个新对象时删除除 5 个最近的对象之外的所有内容。不幸的是,您不能使用 [5:] 来执行此操作。帮助?

EDIT

编辑

I tried this which didn't work as expected (in the model's save method):

我试过这个没有按预期工作(在模型的保存方法中):

    notes = Notification.objects.filter(user=self.user)[:4]
    Notification.objects.exclude(pk__in=notes).delete()

i couldn't find a pattern in strange behavior, but after a while of testing, it would only delete the most recent one when a new one was created. i have NO idea why this would be. the ordering is taken care of in the model's Meta class (by timestamp descending). thanks for the help, but my way seems to be the only one that works consistently.

我找不到奇怪行为的模式,但是经过一段时间的测试,它只会在创建新模式时删除最近的模式。我不知道为什么会这样。排序在模型的 Meta 类中处理(按时间戳降序)。感谢您的帮助,但我的方式似乎是唯一一种始终有效的方式。

回答by Jorge Leitao

This is a bit old, but I believe you can do the following:

这有点旧,但我相信你可以做到以下几点:

notes = Notification.objects.filter(user=self.user)[:4]
Notification.objects.exclude(pk__in=list(notes)).delete()  # list() forces a database hit.

It costs two hits, but avoids using the for loop with transactions middleware.

它需要两次命中,但避免将 for 循环与事务中间件一起使用。

The reason for using list(notes)is that Django creates a single query without it and, in Mysql 5.1, this raises the error

使用的原因list(notes)是 Django 创建了一个没有它的查询,在 Mysql 5.1 中,这会引发错误

(1235, "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'")

By using list(notes), we force a query of notes, avoiding this. This can be further optimized to:

通过使用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()

回答by istruble

Use an inner query to get the set of items you want to keep and then filter on them.

使用内部查询获取要保留的项目集,然后对其进行过滤。

objects_to_keep = Notification.objects.filter(user=user).order_by('-created_at')[:5]
Notification.objects.exclude(pk__in=objects_to_keep).delete()

Double check this before you use it. I have found that simpler inner queries do not always behave as expected. The strange behavior I have experienced has been limited to querysets that are just an order_by and a slice. Since you will have to filter on user, you should be fine.

使用前请仔细检查。我发现更简单的内部查询并不总是按预期运行。我所经历的奇怪行为仅限于只是一个 order_by 和一个切片的查询集。由于您必须对用户进行过滤,因此您应该没问题。

回答by Brandon Henry

this is how i ended up doing this.

这就是我最终这样做的方式。

    notes = Notification.objects.filter(user=self.user)
    for note in notes[4:]:
        note.delete()

because i'm doing this in the save method, the only way the loop would ever have to run more than once would be if the user got multiple notifications at once. i'm not worried about that happening (while it may happen it's not likely to be enough to cause a problem).

因为我是在 save 方法中执行此操作,所以循环必须多次运行的唯一方法是用户一次收到多个通知。我不担心这种情况的发生(虽然它可能发生,但不太可能导致问题)。