简体   繁体   中英

Sort queryset by added field

I'm trying to add an extra field to the instances of my queryset and then sort the set by the new field. But I get a field error( Cannot resolve keyword 'foo' into field. Choices are: ... )

My view(abstract):

def view(request):
    instances = Model.objects.all()
    for counter, instance in enumerate(instances):
        instance.foo = 'bar' + str(counter)
    instances.order_by('foo')  #this line causes trouble
    return render(request, 'template.html', {'instances': instance})

My template:

{% for instance in instances %}
    {{instance.foo}}
{% endfor %}

If I leave out the order_by line the templates renders as expected, so the field seems to be there.

So why do I get a field error?
It would be awesome, if somebody could help me to understand what I'm doing wrong.

Thanks in advance.


I found a possible solution to change the template to

{% for instance in instances|dictsort:'foo' %}

and that works fine, but from what I understand there should be as little logic as possible in the view, so I figure sorting should be done in the view.
Or is this actually the right way?

The Django ORM aims to construct database queries. As a result, you can only query on what a database "knows". Methods, properties, or attributes you added yourself are unknown to the database. The .order_by thus has no effect, since you "patched" the objects in the instances queryset.

If you however call an instances.order_by you construct a new queryset. This queryset takes the context of the parent, and thus represents a (slightly) modified query , but again, a query. Whether the old queryset is already evaluated or patched, is of no importance.

Furthermore even if there was a column foo , it would not help, since the instance.order_by does not order the instance queryset, it constructs a new one, one that looks approximately like the old one, except that the rows are ordered.

You thus will have to sort in Python now. You can for example construct a list of ordered elements with sorted(..) , like:

from operator import 

def view(request):
    instances = Model.objects.all()
    for counter, instance in enumerate(instances):
        instance.foo = 'bar' + str(counter)
    
    return render(request, 'template.html', {'instances': })

So now mydata is no longer a QuerySet , but a vanilla list . Furthermore note that ordering in a database might be slightly different than ordering in Python. In Python exceptions can occur if not all elements have the same type, and furthermore it is not guaranteed that the semantics behind the order relation is exactly the same.

The new attribute in the Python Objects does not exist in the database and only in those instances. order_by changes the queryset and not your current list of objects stored in memory.

One approach would be to use the builtin python sorting functions in the view like: sorted or even list.sort() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM