Granny Aching
Granny Aching

Reputation: 1435

How to get latest and earliest from a django QuerySet

In my django app, there are a few different ways that I get a QuerySet object of the same model.

I would like to use one function to render the data -- and this has been working fine, until I decided to add both earliest and latest time to display on top of the list.

Here's my latest attempt:

def render_list(request, objList):
    latest = earliest = None
    if objList:
        latest = objList.latest('time').time
        earliest = objList.earliest('time').time

    context = {'objList': objList,
               'earliest': earliest,
               'latest': latest,
               }
    return render(request, 'list.html', context)

This produces the error message:

TypeError: Cannot reverse a query once a slice has been taken.

Is there a way to get around this problem?

More details: The function render_list is being called by multiple view functions in views.py, for example :

def list_all(request):
    objList = MyTable.objects.order_by('ip')
    return render_list(request,objList)

def detail(request, ip, group):
    objList = MyTable.objects
    try:
        item = MyTable.objects.get(ip=ip)
        # display the details page...

    except MyTable.DoesNotExist:
        objList = MyTable.objects.complex_filter(...)
        # more code
        # ...
        return render_list(request, objList)

and corresponding lines in urls.py

path('list', views.list_all)
path('<group>/<ip>', views.detail)

Upvotes: 0

Views: 3865

Answers (1)

Endre Both
Endre Both

Reputation: 5740

The way to get around this error is to use latest() and earliest() (as well as first() and last() only on querysets that haven't been sliced (e.g. using queryset[:5] to get the top five records). You objList seems to have been sliced before it was passed to the function.

If you have a queryset that has the records in the correct order (meaning that the earliest and latest are first and last), you have two more options:

  1. Use three additional queries to get the first and last record:
earliest = objList[0]
latest = objList[objList.count()]
  1. Instantiate the objects in the queryset (calling len on it does that). Then you can access the first and last item without additional queries:
latest = objList[len(objList)-1]
earliest = objList[0]

This needs only one query, but it loads all objects in the queryset into memory.

To repeat, this only works if the queryset is ordered so as to have earliest and latest first and last. Otherwise you have to define separate querysets.

Upvotes: 1

Related Questions