根据Django中的项目分组进行分页

时间:2009-03-12 13:36:20

标签: python sql mysql django django-models

对于在Django / Python中实现的网站,我们有以下要求:

在视图页面上,每个网页分页显示15条消息。如果来自同一来源的更多两条或更多条消息在视图上相互跟随,则应将它们组合在一起。

也许不清楚,但以下例子可能是:

一个例子是(这次在页面上有5条消息):

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

这应显示为:

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

因此,在每个页面上,页面上会显示固定数量的项目,其中一些项目已重新分组。

我们想知道如何创建Django或MySQL查询以便以最佳和简单的方式查询这些数据。请注意,使用分页并且消息按时间排序。

PS:由于SQL的性质,我认为没有一个简单的解决方案,但有时可以轻松解决复杂的问题

2 个答案:

答案 0 :(得分:3)

我认为没有任何好方法可以直接做你想做的事情。如果您愿意接受一点反规范化,我会建议使用预保存信号将消息标记为头部。

#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()

然后你的查询变得非常简单:

messages = Message.objects.filter(head=True).order_by('time')[0:15]

说实话......信号监听器必须比我写的那个复杂一点。我的方法中存在大量丢失的同步/丢失更新问题,其解决方案将根据您的服务器而有所不同(如果它是单处理的,多线程的,那么python Lock对象应该可以帮到您by,但如果它是多处理的,那么你真的需要实现基于文件或数据库对象的锁定)。此外,您当然还必须编写相应的删除信号监听器。

显然,这个解决方案涉及添加一些数据库命中,但它们处于编辑状态而不是视图状态,这对您来说可能是值得的。否则,也许考虑一个更粗糙的方法:抓住30个故事,在视图中循环,敲掉你不会显示的故事,如果剩下15个,则显示它们,否则重复。绝对是一个可怕的最糟糕的情况,但也许并不是可怕的平均情况?

如果您的服务器配置使用了多线程的单个进程,则Lock或RLock应该可以解决问题。这是一个使用非重入锁定的可能实现:

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()

同样,相应的删除信号也很重要。

编辑:许多或大多数服务器配置(例如Apache)都会预先编写,这意味着有几个进程在进行。在这种情况下,上面的代码将毫无用处。有关如何开始与分叉进程同步的建议,请参阅this page

答案 1 :(得分:1)

我有一个简单但不完美的模板解决方案。在模板中,您可以使用regroup模板标记重新组合记录。重新组合后,您可以隐藏来自同一来源的连续记录:

{% 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 %}

如果不是一件事,这将是完美的:分页。如果你的意思是每页显示15个项目,而在一个页面上,前五个来自一个来源,接下来五个来自另一个,最后五个来自另一个,页面上只有三个可见项目。