2009-03-12 18 views
5

Đối với một trang web được triển khai trong Django/Python, chúng tôi có yêu cầu sau:Phân trang tùy thuộc vào việc nhóm các mục trong Django

Trên trang xem có 15 tin nhắn trên trang web được hiển thị. Khi có nhiều hơn hai hoặc nhiều tin nhắn từ cùng một nguồn, theo dõi nhau trên màn hình, chúng phải được nhóm lại với nhau.

Có lẽ không rõ ràng, nhưng với sự dụ sau nó có thể là:

Một ví dụ được (với 5 tin nhắn trong một trang thời gian này):

Message1 Source1 
    Message2 Source2 
    Message3 Source2 
    Message4 Source1 
    Message5 Source3 
    ... 

này nên được hiển thị như:

Message1 Source1 
Message2 Source2 (click here to 1 more message from Source2) 
Message4 Source1 
Message5 Source3 
Message6 Source2 

Vì vậy, trên mỗi trang, số lượng mục cố định được hiển thị trên trang, trong đó một số mục được nhóm lại.

Chúng tôi đang tự hỏi làm thế nào chúng ta có thể tạo một truy vấn Django hoặc MySQL để truy vấn dữ liệu này một cách tối ưu và dễ dàng. Lưu ý rằng phân trang được sử dụng và các tin nhắn được sắp xếp theo thời gian.

PS: Tôi không nghĩ rằng có một giải pháp đơn giản cho do này đến bản chất của SQL, nhưng đôi khi vấn đề phức tạp có thể dễ dàng giải quyết

Trả lời

3

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.

+0

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% ... –

+0

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

+0

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". –

1

Tôi có một đơn giản, mặc dù không hoàn hảo, mẫu chỉ giải pháp cho điều này. Trong mẫu, bạn có thể nhóm lại các bản ghi bằng cách sử dụng thẻ mẫu regroup. Sau khi tập hợp lại, bạn có thể ẩn các bản ghi liên tiếp từ cùng một nguồn:

{% regroup records by source as grouped_records %} 
{% for group in grouped_records %} 
    {% for item in group.list %} 
    <li{% if not forloop.first %} style="display:none"{% endif %}> 
     {{ item.message }} {{ iterm.source }} 
     {% if forloop.first %} 
     {% ifnotequal group.list|length 1 %} 
      <a href="#" onclick="...">Show more from the same source...</a> 
     {% endifnotequal %}   
     {% endif %} 
    </li> 
    {% endfor %} 
{% endfor %} 

Điều này sẽ hoàn hảo nếu nó không có một thứ: Phân trang. Nếu bạn muốn hiển thị 15 mục trên mỗi trang và trên một trang, năm trang đầu tiên là nguồn không có nguồn gốc, năm trang tiếp theo từ mã khác, và năm trang tiếp theo khác, sẽ chỉ có ba mục hiển thị trên trang.

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