76

I have a handful of records that I would like to sort based on a computed value. Got the answer over here... like so:

sorted(Profile.objects.all(), key=lambda p: p.reputation)

on a Profile class like this:

class Profile(models.Model):

    ...

    @property
    def reputation(self):
        ...

Unfortunately the generic view is expecting a queryset object and throws an error if I give it a list.

Is there a way to do this that returns a queryset

or...

Can I convert a list to a queryset somehow? Couldn't find anything like that in the django docs.

I am hoping not to denormalize the data, but I guess I will if I have to.

Update / Answer:

it seems that the only way to get a queryset back is if you can get all of your logic into the sql queries.

When that is not possible, (I think) you need to denormalize the data

Community
  • 1
  • 1
Jiaaro
  • 74,485
  • 42
  • 169
  • 190
  • 5
    Isn't a better question "How can I pass a list (or dictionary) to a generic view"? I assume if you solved that then it wouldn't matter whether you did it by converting to a queryset or not... – Andy Baker Jun 30 '09 at 09:45
  • yes... it is, but I just wanted to know if there was an easy way to convert it to a queryset before I go off forking the generic views ;) – Jiaaro Jun 30 '09 at 16:17
  • 4
    mods, this is not a duplicate. It's a different question to which the answer is, "you can't do that, so you have to do something else." That "something else" is described in the other question. – Jiaaro Aug 06 '15 at 15:32

2 Answers2

115

Ok...this post is now old BUT what you could do is get all the ids of the objects in your list, then perform a model.objects.filter(pk__in=list_of_ids)

neolaser
  • 6,722
  • 18
  • 57
  • 90
  • 4
    This is very slow for large collections, but was valuable in the limited situation in which I needed it to quickly solve a problem. It allows you to use QuerySet methods on lists of objects, which can be nice for succinct code. – Chiara Coetzee Feb 22 '13 at 00:39
  • 44
    The downside of this is that it won't keep the original ordering – yprez Dec 01 '14 at 21:31
  • 3
    This worked well for a quick prototype, but I certainly don't recommend doing it in production, for the reasons @Blixt specified. – kqr Dec 05 '14 at 13:13
  • 3
    `MyModel.objects.filter(id__in={instance.id for instance in instances})` – Rik Schoonbeek Jan 07 '22 at 10:34
  • If you want to preserve the list's ordering, see some impressive django-sql-fu at https://stackoverflow.com/a/61686789/303056 – Leopd Sep 06 '22 at 23:55
31

There is no point in converting a data list back to a query. A query object never holds data; it just represents a query to the database. It would have to fetch everything again if you made your list to a query, and that would be redundant and very bad performance-wise.

What you can do:

  • Describe how the reputation field is calculated; it's probably possible to order the data in the database somehow.
  • Modify the view to not require a query object. If it needs to do additional filtering etc. this should be done before any ordering, since the ordering will take less time with less entries (and less data will be fetched from the database.) So you could send the filtered query object to the sort function just before you send it to the template (which shouldn't care whether it's a query or a list.)
Blixt
  • 49,547
  • 13
  • 120
  • 153
  • 4
    reputation is a property, not a field... so that is not valid – Jiaaro Jun 29 '09 at 13:05
  • 3
    Well in that case, you should either store it as a field (redundant data is not always bad if it helps performance, even in normalized databases) or you should order by something else. I'm assuming it's a calculated or joined field, and you can sort by those too with the Django database model. – Blixt Jun 29 '09 at 13:07
  • just had an idea... since the generic view wants to do some kind of filtering, why don't I just do the sorting with a template tag? – Jiaaro Jun 29 '09 at 15:55
  • If you don't have access to the view, and cannot wrap it or in some other way affect the query set before it is sent to the template, then that is certainly an alternative. I don't know if the dictsort filter is implemented to support model instances, but if it isn't you can always make your own. – Blixt Jun 29 '09 at 16:18
  • 5
    *There is no point in converting a data list back to a query.* Well, I found a usecase. In a particular piece of code I'm writing, some complex processing is done using the model objects as values in a dict, and then later we want to update some fields on these items. It would be nice to have them as a queryset to `.update()` them. But probably by that point the data has been separated from the query that formed the set, and it would be impractical to reconstruct that in the new QuerySet. – rschwieb Feb 20 '17 at 21:44
  • 5
    You need to do this if you need to supply a QuerySet to Django REST, django-graphene, paginators or any other api that requires a QuerySet. The list of objects may come from a cache or be filtered and sorted in python (not the db). We should just write something that takes a list and quacks like a QuerySet. – Chris Sattinger May 15 '17 at 10:13
  • There is the point to have .filter(), order_by() and other basic QuerySet methods for custom list classes, first of all these methods are more powerful than built-in Python list processing, second these make the possibility to use lists as queryset arguments of ListView and related classes. That's why I implemented ListQuerySet in django-jinja-knockout, although it is not the part of Django core, thus is not guaranteed to be compatible with the future versions. – Dmitriy Sintsov Sep 10 '17 at 20:17
  • 3
    There is a clear use case. When By going back to QuerySet you can save big on number of queries. Do one instead of thousands and speed up stuff considerably. In my case it went from over 90s to under 1s – juan Isaza Jun 11 '18 at 04:24
  • What if I have a list of items to get deleted or updated from `serializers.PrimaryKeyRelatedField` so that i can do that in a single query? – jerinisready Nov 13 '20 at 11:50
  • 1
    If you are in drf and you need to pass `filter_fields` with url parameters it needs to be queryset. – Elias Prado Jan 06 '22 at 20:46
  • It is interesting how a 13 year old post has relevance. I have a use case for a ListView where I need to make a conditional query to build a QuerySet and implement Pagination. Without being able to build this query through `def get_queryset()` I would be unable to build a Paginated ListView to deliver on this requirement. Any comment that starts, "Never..." in programming is rarely helpful. This particular answer is profoundly off-base, and had I heeded this advice I am not sure I would been able to meet this requirement at all. – Carewen Jun 20 '22 at 19:57