2009-11-07 9 views
5

Tengo una página basada en un objeto modelo, y quiero tener enlaces a las páginas anterior y siguiente. No me gusta mi solución actual porque requiere evaluar todo el conjunto de consultas (para obtener la lista ids) y luego dos consultas más de get. Seguramente hay alguna forma en que esto se puede hacer en una sola pasada.¿Cómo puedo obtener objetos anteriores y siguientes de un queryset filtrado y ordenado?

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) 

Orden de clasificación se define en el modelo, por lo que no tiene que ser manejado aquí, pero tenga en cuenta que no se puede asumir que los identificadores son secuenciales.

Respuesta

8

Suena como algo que el Paginator estableció en un umbral de 1 haría bien en.

# 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 y here.

+1

Gracias, yo era vagamente consciente de la Paginator, pero no había dado cuenta de que podría utilizar de esa manera (es decir, con un umbral de 1, de hecho acaba de envolver el conjunto de consultas en un objeto Paginator con los métodos 'has_next()', 'has_previous()'). Lo único que falta es que no empiezo al principio del rango del paginador, pero parece que puedo obtenerlo con 'p.object_list.index (start_page)'. –

+1

Esto no funcionó bien para mí. La clase Paginator realmente no está hecha para atravesar objetos. –

1

Soy nuevo en Python y Django, así que tal vez mi código no es óptima, pero mira esto:

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) 

Y en vista algo así:

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, 
    }) 

Definitivamente no es óptima para conjuntos de consulta "pesados", pero en la mayoría de los casos funciona como un encanto. :)

1

Echa un vistazo django-next-prev, lo escribí para resolver este problema exacto. En este caso:

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) 
Cuestiones relacionadas