简体   繁体   中英

How can I order a QuerySet in Django by a charfield with numbers?

I want to order a QuerySet by a charfield with numbers in it. I have this code:

MyTable.objects.all().order_by('my_char_field')

There are some examples of "my_char_field":

"ver3", "ver10", "x5.1 (1)", "ver4", "x5.1 (2)"

The result of the order with the code above is:

"ver10", "ver3", "ver4", "x5.1 (1)", "x5.1 (2)"

But the order that I want is:

"ver3", "ver4", "ver10", "x5.1 (1)", "x5.1 (2)"

How can I get the natural order?

It seems more like a data storage issue. It becomes much easier if you change your data structure

version_type = models.CharField()
version_num = models.FloatField()
version_revision = models.SmallIntegerField(null=True)

unique_together = [('version_type', 'version_num', 'version_revision')]

.order_by('version_type', 'version_num', 'version_revision')

This would also solve your next issue of "how can I efficiently get all versions of type 'ver'?"

While it's possible to fetch and do the sorting in the client side, you can also use the Substr function to do this at the server side.

from django.db.models.functions import Substr

MyTable.objects.all().order_by(Substr('my_char_field',3))

Update:

I was working on another update to the answer, when the question was updated yet again!! The following is not complete, but you can build on it.

MyTable.objects.annotate(
    sort_field=Case(
        When(my_char_field__start_with='ver', Then=Substr(my_char_field, 3)),
        When(my_char_field__start_with='x', Then=Substr(my_char_field, 1)),
        default=my_char_field,
    )

).order_by('sort_field')

You will need to use the Cast function as well to cast the sort_field to int. This is going to be one long query!!

I have a CharField with a name field, which has mixed (int+string) values, for example. "a1", "f65", "P", "55" etc ..

Solved the issue by using the sql cast (tested with postgres & mysql), first, I try to sort by the casted integer value, and then by the original value of the name field.

parking_slots = ParkingSlot.objects.all().extra(
        select={'num_from_name': 'CAST(name AS INTEGER)'}
    ).order_by('num_from_name', 'name')

This way, in any case, the correct sorting works for me.

Just as @AKS mentioned, It could be inefficient to do the sorting in memory. As soon as you have a large table and you want to pull only the first page of results sorted. Then you have to pull every single row from the database before you can do the sort.


Maybe you could try to add another field which populate from the "sorting" field when you add a new record. This time, you could make the database sorting works for you.

Something From Vem3456 to Vem6543

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