I’m trying to add some extra attributes to the elements of a QuerySet so I can use the extra information in templates, instead of hitting the database multiple times. Let me illustrate by example, assuming we have Books with ForeignKeys to Authors.
>>> books = Book.objects.filter(author__id=1)
>>> for book in books:
... book.price = 2 # "price" is not defined in the Book model
>>> # Check I can get back the extra information (this works in templates too):
>>> books[0].price
2
>>> # but it's fragile: if I do the following:
>>> reversed = books.reverse()
>>> reversed[0].price
Traceback (most recent call last):
...
AttributeError: 'Book' object has no attribute 'price'
>>> # i.e., the extra field isn't preserved when the QuerySet is reversed.
So adding attributes to the elements of a QuerySet works, so long as you don’t use things like reverse().
My current workaround is to just use select_related() to fetch the extra information I need from the database again, even though I already have in memory.
Is there a better way to do it?
The error arises because qs.reverse() give rise to a new QuerySet instance, so you are not reversing the old one.
If you want to have a base QS on which to act you can do the following:
Of course the
selectkeyword can be much more complex, in fact it can be almost any any meaningful SQL snippet that could fit the[XXX]inEDIT
As pointed out in other responses, this solution may be inefficient.
If you are sure to get all the Book objects from the query, then you ‘d better make one query, store it into a list and eventually reverse the resulting list.
If, on the other hand, you are getting the “head” and the “queue” of the table, making two queries is better, because you won’t query all the “middle” useless objects.