2009-12-05 46 views
21

Tôi có một mô hình django siêu đơn giản ở đây:Django Xóa tất cả nhưng cuối cùng năm người queryset

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

Sử dụng ajax, tôi kiểm tra thư mới mỗi phút. Tôi chỉ hiển thị năm thông báo gần đây nhất cho người dùng bất kỳ lúc nào. Những gì tôi đang cố gắng tránh, là kịch bản sau đây.

Nhật ký người dùng và không có thông báo. Khi cửa sổ của người dùng bật lên, anh ta nhận được 10 tin nhắn mới. Vì tôi chỉ cho anh ta xem năm, không có vấn đề gì. Sự cố xảy ra khi người dùng bắt đầu xóa thông báo của anh ấy. Nếu anh ta xóa năm được hiển thị, năm cái cũ hơn sẽ được hiển thị trên cuộc gọi ajax tiếp theo hoặc làm mới.

Tôi muốn phương pháp lưu của mô hình của tôi xóa mọi thứ trừ 5 đối tượng gần đây nhất bất kỳ khi nào một đối tượng mới được lưu. Thật không may, bạn không thể sử dụng [5:] để làm điều này. Cứu giúp?

EDIT

Tôi cố gắng này mà không làm việc như mong đợi (trong phương pháp tiết kiệm của mô hình):

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

tôi không thể tìm thấy một mẫu trong hành vi kỳ lạ, nhưng sau một trong khi thử nghiệm, nó sẽ chỉ xóa một trong những gần đây nhất khi một cái mới được tạo ra. tôi KHÔNG biết tại sao điều này lại xảy ra. thứ tự được quan tâm trong lớp Meta của mô hình (theo dấu thời gian giảm dần). cảm ơn sự giúp đỡ, nhưng cách của tôi dường như là người duy nhất hoạt động liên tục.

+0

hữu ích để nhấn mạnh rằng thực tế là bạn không thể sử dụng xóa trên một lát được giải thích trong doc: https: //docs.djangoproject.com/en/dev/ref/mô hình/querysets/# xóa - nó làm tăng một 'Không thể sử dụng 'giới hạn' hoặc 'bù đắp' với lỗi xóa'. Tôi đã hy vọng rằng kể từ '09 một cái gì đó đã thay đổi nhưng "cho" giải pháp vẫn có vẻ là người duy nhất! – Stefano

Trả lời

20

Đây là một chút cũ, nhưng tôi tin rằng bạn có thể làm như sau:

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

Chi phí hai hit, nhưng tránh sử dụng cho vòng lặp với các giao dịch trung gian.

Lý do là Django tạo ra một truy vấn duy nhất, và trong Mysql 5.1, điều này đặt ra lỗi

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

Bằng cách sử dụng list(notes), chúng tôi buộc một truy vấn của notes, tránh này. Điều này có thể được tiếp tục tối ưu hóa để:

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

Tôi thậm chí không có một môi trường django nữa, nhưng tôi tin rằng đây là một giải pháp tốt hơn so với vòng lặp của tôi. –

+1

Trên tập dữ liệu lớn hơn, có khả năng là giải pháp tốt hơn nhưng vòng lặp for dễ đọc hơn theo ý kiến ​​của tôi. – radtek

11

Sử dụng truy vấn bên trong để nhận tập hợp các mục bạn muốn giữ và sau đó lọc chúng.

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

Kiểm tra kỹ trước khi sử dụng. Tôi đã thấy rằng các truy vấn bên trong đơn giản hơn không phải lúc nào cũng hoạt động như mong đợi. Hành vi lạ mà tôi gặp phải đã bị hạn chế đối với các querys chỉ là một order_by và một slice. Vì bạn sẽ phải lọc người dùng, bạn sẽ ổn thôi.

+0

tôi đã thử một phương pháp tương tự như thế này và nó đã được kỳ quặc. khi nó đến một điểm nào đó nó đã xóa tất cả các đối tượng với người dùng. tôi tin rằng nó có một cái gì đó để làm với cách slicing làm việc với querys lười biếng. –

+0

Thêm mã mà bạn đã thử. Tôi sẽ đánh giá cao nhiều điểm dữ liệu hơn cho hành vi kỳ quặc. Đối với tôi, order_by dường như bị tước bỏ truy vấn bên trong nếu không có bộ lọc(). Bạn có kinh nghiệm hành vi lạ với một Model.objects.filter (...) orber_by (...) [: 5] truy vấn bên trong? Ồ, và bạn có thể tìm thấy .query.as_sql() hữu ích. – istruble

+0

Tôi đã chỉnh sửa câu hỏi của mình bằng mã tôi đã thử. Tôi đã không có mã mà đã xóa tất cả các đối tượng, nhưng tôi đã thử mã của tôi ở trên (thậm chí nhiều hơn tương tự như của bạn), và điều đó đã không làm việc như tôi đã mong đợi. –

8

đây là cách tôi kết thúc việc này.

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

vì tôi đang làm điều này trong phương thức lưu, cách duy nhất vòng lặp sẽ chạy nhiều lần nếu người dùng có nhiều thông báo cùng một lúc. Tôi không lo lắng về điều đó xảy ra (trong khi nó có thể xảy ra nó không có khả năng là đủ để gây ra một vấn đề).

+1

Điều này hoàn toàn hợp lệ. Và bạn đã nhận thức được thực tế là bạn sẽ nhận được một hoạt động sql cho mỗi lần xóa trong vòng lặp for. Cảm ơn các cập nhật. – istruble

+9

Phá vỡ vài dòng trên trên một phương thức và thêm một trang trí commit_on_success xung quanh nó, đảm bảo nó vẫn hoạt động nhanh mặc dù có nhiều truy vấn được thực hiện. –

Các vấn đề liên quan