2010-04-05 22 views
12

Tôi ngạc nhiên khi câu hỏi này chưa xuất hiện. Không thể tìm thấy nhiều trên web.Tương đương Django cho mục nhập mới nhất cho mỗi người dùng

Sử dụng Entry.objects.latest('created_at') Tôi có thể khôi phục mục nhập mới nhất cho tất cả các mục nhập, nhưng nếu tôi muốn mục nhập mới nhất cho từng người dùng? Đây là một cái gì đó tương tự như một truy vấn bản ghi SQL mới nhất. Nhưng làm thế nào để tôi đạt được điều này bằng cách sử dụng ORM? Đây là cách tiếp cận của tôi, tôi tự hỏi nếu đó là cách hiệu quả nhất để làm những gì tôi muốn.

Đầu tiên tôi thực hiện truy vấn phụ: Đối tượng được nhóm theo người dùng và trường Max (mới nhất) created_by được trả về cho mỗi người dùng (created_at__max), sau đó lọc đối tượng Nhập dựa trên kết quả trong truy vấn phụ và nhận các đối tượng được yêu cầu.

Entry.objects.filter(created_at__in=Entry.objects.values('user').annotate(Max('created_at')).values_list('created_at__max')) 

hoặc sử dụng một người quản lý:

class UsersLatest(models.Manager): 

    def get_query_set(self): 
     return super(UsersLatest,self).get_query_set().filter(created_at__in=self.model.objects.values('user').annotate(Max('created_at')).values_list('created_at__max')) 

Có cách nào hiệu quả hơn? có thể không có truy vấn phụ?

Cảm ơn,

Paul

+1

Tôi đã đùa giỡn với ý tưởng thêm một boolean is_latest vào mô hình của tôi, mà tôi đặt khi tôi lưu một mục mới trong phương thức form.save của tôi, sau đó khôi phục đối tượng is_latest trước đó và làm cho nó sai --- vậy là hai bước cho mỗi mục lưu. Tôi không biết đây có phải là cách tốt nhất để đi hay không. Bất kỳ đề xuất? – Paul

Trả lời

0

Tôi không thể nghĩ ra một truy vấn sql liệu duy nhất mà sẽ lấy các thiết lập mà bạn cần, vì vậy tôi nghi ngờ nó có thể để xây dựng một QuerySet với những kết quả này.

1

Thiết kế QuerySet của bạn tùy thuộc vào những gì bạn dự định sử dụng. Tôi không chắc tại sao bạn lại thoát khỏi bộ lặp QuerySet với phương thức values_list ở cuối. Tôi tưởng tượng bạn có danh sách trạng thái người dùng nơi bạn hiển thị thời gian hoạt động cuối cùng dựa trên mô hình Entries đó. Cho rằng bạn có thể muốn thử điều này:

Users.objects.all().annotate(latest_activity=Max('entries__created_at')) 

Và sau đó vòng qua người dùng của bạn một cách dễ dàng trong mẫu của bạn với

{% for user in users %} 
{{ user.full_name }} 
{{ user.latest_activity|date: "m/d/Y" }} 
{% endfor %} 
+0

Xin cảm ơn lời khuyên của bạn, cách tiếp cận tốt đẹp. chú thích (Max ('created_at')) trả về danh sách từ điển chứa dữ liệu 'người dùng' và 'created_at__max', tôi sử dụng values_list ('created_at__max') để lọc dữ liệu vào danh sách để sử dụng với created_at__in = – Paul

1

SQL thô sẽ

SELECT entry.id, entry.title, entry.content, entry.user_id, entry.created_at 
FROM 
    entry 
WHERE 
    entry.created_at = (SELECT Max(e2.created_at) from entry as e2 where e2.user_id = entry.user_id) 

Vì vậy, một lựa chọn là sử dụng đối số where của extra() modifier:

Entry.objects.extra(where='entry.created_at = (SELECT Max(e2.created_at) from entry as e2 where e2.user_id = entry.user_id)') 

Tất nhiên, bạn có thể phải thay đổi entry thành bất kỳ tên thực tế nào của bảng trong cơ sở dữ liệu. Giả sử bạn cảm thấy thoải mái nhìn ._meta, bạn có thể thử này:

Entry.objects.extra(where= 
    '%(table)s.created_at = (SELECT Max(e2.created_at) from %(table)s as e2 where e2.user_id = %(table)s.user_id)' % { 'table':Entry._meta.db_table } 
) 

Có lẽ một cách thanh lịch hơn để có được tên của một bảng.

+0

Cảm ơn đề xuất , thực hiện chính xác những gì tôi yêu cầu. Tôi đã kiểm tra sql của truy vấn Django của tôi bằng cách sử dụng ._as_sql() và nó gần như giống hệt nhau – Paul

0

Tôi đã có một vấn đề tương tự và đã làm nó theo cách này:

priorities = obj.books_set.values('category').annotate(priority=Max('priority')) 

Lưu ý: tôi chú thích ưu tiên tối đa là ưu tiên, bởi vì tôi sẽ tái sử dụng các đầu ra như điều kiện lọc.

Đó là danh sách các danh mục có mức độ ưu tiên tối thiểu.Sau đó, tôi thực hiện việc này:

>>> priorities[0] 
{'category': 1, 'priority': 10} 

Tôi muốn tìm các sách có loại ưu tiên là & trong số đó. Cuối cùng điều kiện queryset sẽ trông như thế này:

Q(priorities[0]) | Q(priorities[1]) | ... 

Để làm điều này trong một dòng, sử dụng reduce trên Q.__or__:

reduce(Q.__or__, (Q(**x) for x in priorities)) 

Tôi biết đó là một chút tồi tệ hơn SQL thô, nhưng an toàn hơn. Bình luận mã này nếu bạn sử dụng nó, bởi vì nó khó đọc.

3

Sử dụng order_bydistinct:

Entry.objects.all().order_by('user', 'created_at').distinct('user') 

Sau đó, để thực hiện thêm chỉ số với nhau trên 'sử dụng' và 'created_at' lĩnh vực.

Nhưng tôi nghĩ cách sản xuất thực tế là sử dụng Redis để lưu vào bộ nhớ cache và cập nhật danh sách các mục nhập mới nhất của người dùng.

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