Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Delete all but last five of queryset

Tags:

python

django

I have a super simple django model here:

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.

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.

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?

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.

like image 370
Brandon Henry Avatar asked Dec 05 '09 05:12

Brandon Henry


People also ask

How do I delete multiple items in Django?

The django querysets where we use filter() function to select multiple rows and use delete() function to delete them is also known as bulk deletion.

How do I remove all objects from a Django model?

If you want to remove all the data from all your tables, you might want to try the command python manage.py flush . This will delete all of the data in your tables, but the tables themselves will still exist.

How do I remove an object from QuerySet?

queryset acts just like a list so you can . pop() items and del() them.


2 Answers

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() 
like image 118
Jorge Leitao Avatar answered Sep 19 '22 01:09

Jorge Leitao


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.

like image 22
istruble Avatar answered Sep 18 '22 01:09

istruble