Paginação dependendo agrupamento de itens em Django

votos
5

Para um site implementado em Django / Python, temos o seguinte requisito:

Em uma página de vista existem 15 mensagens por paging web mostrados. Quando há mais duas ou mais mensagens da mesma fonte, que se seguem uns aos outros sobre a vista, eles devem ser agrupados.

Talvez não está claro, mas com as seguintes exemple, pode ser:

Um exemplo é (com 5 mensagens em uma página desta vez):

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

Isso deve ser mostrado como:

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

Então, em cada página um número fixo de itens é mostrado na página, onde alguns foram reagrupadas.

Estamos querendo saber como podemos criar uma consulta de Django ou MySQL para consultar esses dados em um ideal e de uma forma fácil. Note-se que paginação é usado e que as mensagens são classificadas segundo tempo.

PS: Eu não acho que há uma solução simples para isso, devido à natureza de SQL, mas às vezes problemas complexos pode ser facilmente resolvido

Publicado 12/03/2009 em 14:36
fonte usuário
Em outras línguas...                            


2 respostas

votos
3

Não vejo qualquer grande maneira de fazer o que você está tentando fazer diretamente. Se você está disposto a aceitar um pouco de-normalização, eu recomendaria um pré-salvar sinal para marcar as mensagens como sendo na cabeça.

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

Em seguida, sua consulta torna-se magicamente simples:

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

Para ser franco ... o ouvinte sinal teria que ser um pouco mais complicado do que o que eu escrevi. Há uma série de sincronização perdido / perdido problemas de atualização inerentes a minha abordagem, as soluções para o que irá variar dependendo do seu servidor (se for single-processados, multi-threaded, em seguida, uma python Lockobjeto deve começar por você, mas se é multi-processados, então você vai realmente precisa para implementar bloqueio com base em arquivos ou objetos de banco de dados). Além disso, você vai, certamente, também tem que escrever um ouvinte de sinal de exclusão correspondente.

Obviamente, esta solução envolve a adição de alguns hits de banco de dados, mas eles estão em editar ao contrário de vista, que poderia ser interessante para você. Caso contrário, talvez considerar uma abordagem mais crua: agarrar 30 histórias, loop através da na vista, bater para fora os que você não vai mostrar, e se você tem 15 esquerdo, exibi-los, caso contrário repetir. Definitivamente um cenário de pior caso horrível, mas caso médio, talvez, não é terrível?

Se você tivesse uma configuração de servidor que é utilizado um único processo que é multi-threaded, um bloqueio ou RLOCK deve fazer o truque. Aqui está uma possível implementação com bloqueio não reentrante:

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

Mais uma vez, um sinal de eliminação correspondente é crítica bem.

EDIT: Muitos ou a maioria das configurações do servidor (como o Apache) vai prefork, ou seja, há vários processos em curso. O código acima será inútil nesse caso. Veja esta página para ter idéias de como começar a sincronização com processos bifurcados.

Respondeu 12/03/2009 em 15:20
fonte usuário

votos
1

Eu tenho um simples, embora não seja perfeito, solução apenas de modelo para isso. No modelo que você pode agrupar os registros usando a regrouptag. Depois de reagrupamento pode ocultar registros sucessivos da mesma fonte:

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

Isso seria perfeito se não fosse por uma coisa: Paginação. Se você quer dizer para exibir 15 itens por página, e em uma página os cinco primeiros são fonte fromone, próximos cinco de outro, e os últimos cinco mais um, haveria apenas três itens visíveis na página.

Respondeu 12/03/2009 em 15:05
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more