2009-11-07 10 views
5

J'ai une page basée sur un objet modèle, et je veux avoir des liens vers les pages précédentes et suivantes. Je n'aime pas ma solution actuelle car elle nécessite l'évaluation de l'ensemble du jeu de requête (pour obtenir la liste ids), puis deux autres requêtes get. Sûrement il y a un moyen de le faire en un seul passage?Comment puis-je obtenir les objets précédents et suivants à partir d'un jeu de requête ordonné filtré?

def get_prev_and_next_page(current_page): 
    ids = list(Page.objects.values_list("id", flat=True)) 
    current_idx = ids.index(current_page.id) 
    prev_page = Page.objects.get(id=ids[current_idx-1]) 
    try: 
     next_page = Page.objects.get(id=ids[current_idx+1]) 
    except IndexError: 
     next_page = Page.objects.get(id=ids[0]) 
    return (prev_page, next_page) 

Ordre de tri est défini dans le modèle, donc n'a pas à être traitées, mais sachez que vous ne pouvez pas supposer que ids sont séquentiels.

Répondre

8

Cela ressemble à quelque chose que le Paginator a fixé à un seuil de 1.

# Full query set... 
pages = Page.objects.filter(column=somevalue) 
p = Paginator(pages, 1) 

# Where next page would be something like... 
if p.has_next(): 
    p.page(p.number+1) 

Documentation here et here.

+1

Merci, je suis vaguement au courant de la Paginator, mais n'avait pas rendu compte que je pouvais l'utiliser comme ça (ie avec un seuil de 1, en effet enveloppant juste le queryset dans un objet Paginator avec les méthodes 'has_next()', 'has_previous()'). La seule chose qui manque est que je ne commence pas au début de la plage du paginateur, mais il semble que je puisse l'obtenir avec 'p.object_list.index (start_page)'. –

+1

Cela a fini par ne pas bien fonctionner pour moi. La classe Paginator n'est vraiment pas faite pour traverser des objets. –

1

Je suis nouveau à Python et Django, alors peut-être mon code n'est pas optimal, mais de vérifier cela:

def get_prev_and_next_items(target, items): 
    ''' To get previous and next objects from QuerySet ''' 
    found = False 
    prev = None 
    next = None 
    for item in items: 
     if found: 
      next = item 
      break 
     if item.id == target.id: 
      found = True 
      continue 
     prev = item 
    return (prev, next) 

Et vu quelque chose comme ça:

def organisation(request, organisation_id): 
    organisation = Organisation.objects.get(id=organisation_id) 
    ... 
    prev, next = get_prev_and_next_items(organisation, Organisation.objects.all().order_by('type')) 
    ... 
    return render_to_response('reference/organisation/organisation.html', { 
     'organisation': organisation, 
     'prev': prev, 
     'next': next, 
    }) 

Certainement pas optimale pour les querysets «lourds», mais dans la plupart des cas fonctionne comme un charme. :)

1

Check out django-next-prev, je l'ai écrit pour résoudre ce problème. Dans ce cas:

from next_prev import next_in_order, prev_in_order 

def get_prev_and_next_page(current_page): 
    page_qs = Page.objects.all() # this could be filtered, or ordered 
    prev_page = prev_in_order(current_page, page_qs) 
    next_page = prev_in_order(current_page, page_qs) 
    return (prev_page, next_page)