Tôi không thấy cách nào tuyệt vời để làm những gì bạn đang cố gắng làm trực tiếp. Nếu bạn sẵn sàng chấp nhận một chút bình thường hóa, tôi sẽ khuyên bạn nên sử dụng tín hiệu lưu trước để đánh dấu các tin nhắn như đang ở đầu.
#In your model
head = models.BooleanField(default=True)
#As a signal plugin:
def check_head(sender, **kwargs):
message = kwargs['instance']
if hasattr(message,'no_check_head') and message.no_check_head:
return
previous_message = Message.objects.filter(time__lt=message.time).order_by('-time')[0]
if message.source == previous_message.source:
message.head = False
next_message = Message.objects.filter(time__gt=message.time).order_by('time')[0]
if message.source == next_message.source:
next_message.head = False
next_message.no_check_head
next_message.save()
Sau đó truy vấn của bạn trở nên kỳ diệu đơn giản:
messages = Message.objects.filter(head=True).order_by('time')[0:15]
Để được khá trung thực ... người nghe tín hiệu sẽ phải là một chút phức tạp hơn một tôi đã viết. Có một loạt các vấn đề mất đồng bộ/mất cập nhật vốn có trong cách tiếp cận của tôi, các giải pháp sẽ thay đổi tùy thuộc vào máy chủ của bạn (nếu nó được xử lý đơn, đa luồng, sau đó một đối tượng python Lock
sẽ giúp bạn, nhưng nếu nó là đa xử lý, sau đó bạn thực sự sẽ cần phải thực hiện khóa dựa trên các tập tin hoặc các đối tượng cơ sở dữ liệu). Ngoài ra, bạn chắc chắn cũng sẽ phải viết một trình nghe tín hiệu xóa tương ứng.
Rõ ràng giải pháp này liên quan đến việc thêm một số lần truy cập cơ sở dữ liệu, nhưng chúng được chỉnh sửa trái với chế độ xem, điều này có thể đáng giá đối với bạn. Nếu không, có lẽ xem xét một cách tiếp cận cruder: lấy 30 câu chuyện, vòng qua trong xem, gõ ra những cái bạn sẽ không hiển thị, và nếu bạn có 15 trái, hiển thị chúng, nếu không lặp lại. Chắc chắn một kịch bản xấu nhất tồi tệ, nhưng có lẽ không phải là trường hợp trung bình khủng khiếp?
Nếu bạn có cấu hình máy chủ sử dụng một quy trình đơn lẻ đa luồng, Khóa hoặc RLock sẽ thực hiện thủ thuật.Dưới đây là triển khai có thể có với khóa không có reentrant:
import thread
lock = thread.allocate_lock()
def check_head(sender, **kwargs):
# This check must come outside the safe zone
# Otherwise, your code will screech to a hault
message = kwargs['instance']
if hasattr(message,'no_check_head') and message.no_check_head:
return
# define safe zone
lock.acquire()
# see code above
....
lock.release()
Một lần nữa, một tín hiệu xóa tương ứng cũng rất quan trọng.
EDIT: Nhiều hoặc hầu hết các cấu hình máy chủ (chẳng hạn như Apache) sẽ prefork, có nghĩa là có một số quá trình đang diễn ra. Đoạn mã trên sẽ vô ích trong trường hợp đó. Xem this page để biết các ý tưởng về cách bắt đầu đồng bộ hóa với quy trình được chia hai.
Tôi nghĩ đây là cách tiếp cận chung mà bạn phải thực hiện, mặc dù bạn có thể thay thế các lần truy cập cơ sở dữ liệu bằng cách lưu trữ last_source trong memcached; vẫn có một điều kiện chạy đua tiềm năng dưới sự tương tranh nặng nề, nhưng nếu nó không phải hoàn hảo 100% ... –
Như bạn đã nói, không phải là lựa chọn hoàn hảo, nhưng tôi vẫn nghĩ là tốt nhất. Cảm ơn – Michael
Ngoài ra ... Tôi đoán tôi đã luôn luôn thích sử dụng tín hiệu ... nhưng trọng các phương pháp tiết kiệm có thể làm điều tương tự. Một số người theo chủ nghĩa thuần túy theo kiểu lập luận cho rằng đó là "điều đúng". –