[Django]-Django Delete all but last five of queryset

53👍

✅

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.

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

(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:

notes = Notification.objects.filter(user=self.user)[:4].values_list("id", flat=True)  # only retrieve ids.
Notification.objects.exclude(pk__in=list(notes)).delete()

12👍

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.

11👍

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).

Leave a comment