Obteniendo objetos anteriores y siguientes en Django

Estoy tratando de obtener los objetos siguientes y anteriores de una edición de cómics. Simplemente cambiar el número de identificación o filtrar hasta la fecha agregada no funcionará porque no agrego los problemas secuencialmente.

Así es como se configuran mis vistas y FUNCIONA para prev_issue y devuelve el objeto anterior, pero devuelve el último objeto para next_issue y no se porque.

def issue(request, issue_id):
    issue = get_object_or_404(Issue, pk=issue_id)
    title = Title.objects.filter(issue=issue)
    prev_issue = Issue.objects.filter(title=title).filter(number__lt=issue.number)[0:1]
    next_issue = Issue.objects.filter(title=title).filter(number__gt=issue.number)[0:1]

preguntado el 16 de mayo de 11 a las 18:05

¿Qué está intentando hacer con la información de los números anterior / siguiente? ¿Tiene la intención de vincularlos en una página, mostrarlos en una lista, etc.? ¿Los issue_ids son secuenciales una vez filtrados para cada título? -

solo una pregunta curiosa: ¿estás usando un get_absolute_url atributo en su modelo o simplemente redireccionando a prev_issue.issue_id en la URL? -

Estoy usando el get_absolute_url atributo ... definitivamente no es el issue_id porque no agrego problemas secuencialmente todo el tiempo, y eso no sería práctico. -

5 Respuestas

Añadir un order_by cláusula para asegurar que ordene por number.

next_issue = Issue.objects.filter(title=title, number__gt=issue.number).order_by('number').first()

respondido 12 nov., 18:15

También debe hacer esto con la consulta prev_issue. ¡Feliz de ayudar! - Steve Mayne

Me pregunto cuántos registros recuperaría un comando de este tipo de la base de datos. ¿Solo un registro o todos los registros que cumplen las condiciones de filtrado (y solo elegimos uno de ellos)? Si es el último caso, ¿cómo podemos hacerlo más eficiente? - Randy Tang

La sintaxis de corte ([0: 1]) utilizará consultas de "límite" si es compatible con la base de datos, por lo que debería estar bien en MySQL. docs.djangoproject.com/en/dev/topics/db/queries/… - Steve Mayne

Asegúrese de que el campo 'número' tenga un índice. - sbaechler

No necesita usar el filtro dos veces, puede hacerlo filter(title=title, number__gt=issue.number) - Michael Buckley

Sé que esto es un poco tarde, pero para cualquier otra persona, django tiene una forma mejor de hacer esto, ver https://docs.djangoproject.com/en/1.7/ref/models/instances/#django.db.models.Model.get_previous_by_FOO

Entonces, la respuesta aquí sería algo como

next_issue = Issue.get_next_by_number(issue, title=title)

Los administradores de Django deben hacerlo con un poco de habilidad para las metaclase.

Respondido 11 Feb 15, 00:02

Tenga en cuenta que esto solo funciona para DateField y DateTimeField no nulos. - Rick Westera

Supongo que se hace en el nivel del núcleo de Django debido a las dificultades que el desarrollador podría experimentar debido a la posible igualdad de fecha o fecha y hora. Las condiciones cambian a __gte y __lte, lo que provoca una complejidad adicional a la consulta resultante. Estos incorporados son la mejor opción cuando se trata de objetos anteriores y siguientes por Date* campos. - Orgánico dañado

La mejor solución. Pulgar hacia arriba (y) - Shubho Shaha

Enlace actualizado ya que el original está roto: docs.djangoproject.com/en/3.1/ref/models/instances/… - Eduardo Matsuoka

Si es necesario encontrar siguiente y anterior objetos ordenados por valores de campo que puede ser igual y esos campos no son de Date* type, la consulta se vuelve un poco compleja, porque:

  • ordenar en objetos con los mismos valores limitando por [:1] siempre producirá el mismo resultado para varios objetos;
  • El objeto en sí mismo puede incluirse en el conjunto resultante.

Aquí hay conjuntos de consultas que también tienen en cuenta las claves primarias para producir un resultado correcto (asumiendo que number El parámetro de OP no es único y omite el title parámetro ya que es irrelevante para el ejemplo):

Anterior:

prev_issue = (Issue.objects
    .filter(number__lte=issue.number, id__lt=instance.id)
    .exclude(id=issue.id)
    .order_by('-number', '-id')
    .first())

Siguiente:

next_issue = (Issue.objects
    .filter(number__gte=issue.number, id__gt=instance.id)
    .exclude(id=issue.id)
    .order_by('number', 'id')
    .first())

Respondido el 14 de enero de 17 a las 13:01

from functools import partial, reduce
from django.db import models


def next_or_prev_instance(instance, qs=None, prev=False, loop=False):

    if not qs:
        qs = instance.__class__.objects.all()

    if prev:
        qs = qs.reverse()
        lookup = 'lt'
    else:
        lookup = 'gt'

    q_list = []
    prev_fields = []

    if qs.query.extra_order_by:
        ordering = qs.query.extra_order_by
    elif qs.query.order_by:
        ordering = qs.query.order_by
    elif qs.query.get_meta().ordering:
        ordering = qs.query.get_meta().ordering
    else:
        ordering = []

    ordering = list(ordering)

    if 'pk' not in ordering and '-pk' not in ordering:
        ordering.append('pk')
        qs = qs.order_by(*ordering)

    for field in ordering:
        if field[0] == '-':
            this_lookup = (lookup == 'gt' and 'lt' or 'gt')
            field = field[1:]
        else:
            this_lookup = lookup
        q_kwargs = dict([(f, get_model_attr(instance, f))
                         for f in prev_fields])
        key = "%s__%s" % (field, this_lookup)
        q_kwargs[key] = get_model_attr(instance, field)
        q_list.append(models.Q(**q_kwargs))
        prev_fields.append(field)
    try:
        return qs.filter(reduce(models.Q.__or__, q_list))[0]
    except IndexError:
        length = qs.count()
        if loop and length > 1:
            return qs[0]
    return None


next_instance = partial(next_or_prev_instance, prev=False)
prev_instance = partial(next_or_prev_instance, prev=True)

Respondido 17 Oct 20, 05:10

next_obj_id = int (current_obj_id) + 1 next_obj = Model.objects.filter (id = next_obj_id) .first ()

prev_obj_id = int (current_obj_id) - 1 prev_obj = Model.objects.filter (id = prev_obj_id) .first ()

#No tienes nada que perder aquí ... Esto me funciona

Respondido 03 Jul 21, 16:07

La variable current_obj_id no existe en el código del OP. - Calculuswhiz

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.