28
If you’re sure there are only integers in the field, you could get the database to cast it as an integer via the extra
method, and order by that:
MyModel.objects.extra(
select={'myinteger': 'CAST(mycharfield AS INTEGER)'}
).order_by('myinteger')
23
Django is trying to deprecate the extra()
method, but has introduced Cast()
in v1.10. In sqlite (at least), CAST
can take a value such as 10a
and will cast it to the integer 10
, so you can do:
from django.db.models import IntegerField
from django.db.models.functions import Cast
MyModel.objects.annotate(
my_integer_field=Cast('my_char_field', IntegerField())
).order_by('my_integer_field', 'my_char_field')
which will return objects sorted by the street number first numerically, then alphabetically, e.g. ...14, 15a, 15b, 16, 16a, 17...
- [Django]-Django i18n: Make sure you have GNU gettext tools
- [Django]-How to change site title, site header and index title in Django Admin?
- [Django]-Listing objects from ManyToManyField
19
If you’re using PostgreSQL (not sure about MySQL) you can safely use following code on char/text fields and avoid cast errors:
MyModel.objects.extra(
select={'myinteger': "CAST(substring(charfield FROM '^[0-9]+') AS INTEGER)"}
).order_by('myinteger')
- [Django]-Django templates: verbose version of a choice
- [Django]-AccessDenied when calling the CreateMultipartUpload operation in Django using django-storages and boto3
- [Django]-Phpmyadmin logs out after 1440 secs
4
I know that I’m late on this, but since it’s strongly related to the question, and that I had a hard time finding this:
You have to know that you can directly put the Cast
in the ordering
option of your model.
from django.db import models
from django.db.models.functions import Cast
class Address(models.Model):
street_number = models.CharField()
class Meta:
ordering = [
Cast("street_number", output_field=models.IntegerField()),
]
From the doc about ordering:
You can also use query expressions.
And from the doc about database functions:
Functions are also expressions, so they can be used and combined with other expressions like aggregate functions.
- [Django]-Using AD as authentication for Django
- [Django]-How to filter (or replace) unicode characters that would take more than 3 bytes in UTF-8?
- [Django]-Filter on prefetch_related in Django
3
Great tip! It works for me! That’s my code:
revisioned_objects = revisioned_objects.extra(select={'casted_object_id': 'CAST(object_id AS INTEGER)'}).extra(order_by = ['casted_object_id'])
- [Django]-How to override the django admin translation?
- [Django]-Django best approach for creating multiple type users
- [Django]-Django ImageField change file name on upload
2
The problem you’re up against is quite similar to how filenames get ordered when sorting by filename. There, you want “2 Foo.mp3” to appear before “12 Foo.mp3”.
A common approach is to “normalize” numbers to expanding to a fixed number of digits, and then sorting based on the normalized form. That is, for purposes of sorting, “2 Foo.mp3” might expand to “0000000002 Foo.mp3”.
Django won’t help you here directly. You can either add a field to store the “normalized” address, and have the database order_by
that, or you can do a custom sort in your view (or in a helper that your view uses) on address records before handing the list of records to a template.
- [Django]-How to redirect with post data (Django)
- [Django]-What is dispatch used for in django?
- [Django]-PyCharm Not Properly Recognizing Requirements – Python, Django
2
In case you need to sort version numbers consisting of multiple numbers separated by a dot (e.g. 1.9.0, 1.10.0
), here is a postgres-only solution:
class VersionRecordManager(models.Manager):
def get_queryset(self):
return super().get_queryset().extra(
select={
'natural_version': "string_to_array(version, '.')::int[]",
},
)
def available_versions(self):
return self.filter(available=True).order_by('-natural_version')
def last_stable(self):
return self.available_versions().filter(stable=True).first()
class VersionRecord(models.Model):
objects = VersionRecordManager()
version = models.CharField(max_length=64, db_index=True)
available = models.BooleanField(default=False, db_index=True)
stable = models.BooleanField(default=False, db_index=True)
In case you want to allow non-numeric characters (e.g. 0.9.0 beta
, 2.0.0 stable
):
def get_queryset(self):
return super().get_queryset().extra(
select={
'natural_version':
"string_to_array( "
" regexp_replace( " # Remove everything except digits
" version, '[^\d\.]+', '', 'g' " # and dots, then split string into
" ), '.' " # an array of integers.
")::int[] "
}
)
- [Django]-How to use schemas in Django?
- [Django]-PicklingError: Can't pickle <class 'decimal.Decimal'>: it's not the same object as decimal.Decimal
- [Django]-Django DeleteView without confirmation template
2
In my case i have a CharField with a name field, which has mixed (int+string) values, for example. “a1”, “f65”, “P”, “55” e.t.c ..
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.
- [Django]-What should I use instead of syncdb in Django 1.9?
- [Django]-How to query directly the table created by Django for a ManyToMany relation?
- [Django]-How to have two models reference each other Django
1
I was looking for a way to sort the numeric chars in a CharField
and my search led me here. The name
fields in my objects are CC Licenses, e.g., ‘CC BY-NC 4.0’.
Since extra()
is going to be deprecated, I was able to do it this way:
MyObject.objects.all()
.annotate(sorting_int=Cast(Func(F('name'), Value('\D'), Value(''), Value('g'), function='regexp_replace'), IntegerField()))
.order_by('-sorting_int')
Thus, MyObject
with name='CC BY-NC 4.0'
now has sorting_int=40
.
- [Django]-Django's ModelForm unique_together validation
- [Django]-How do I render jinja2 output to a file in Python instead of a Browser
- [Django]-How to upload a file in Django?
0
All the answeres in this thread did not work for me because they are assuming numerical text. I found a solution that will work for a subset of cases. Consider this model
Class Block(models.Model):
title = models.CharField()
say I have fields that sometimes have leading characters and trailing numerical characters If i try and order normally
>>> Block.objects.all().order_by('title')
<QuerySet [<Block: 1>, <Block: 10>, <Block: 15>, <Block: 2>, <Block: N1>, <Block: N12>, <Block: N4>]>
As expected, it’s correct alphabetically, but makes no sense for us humans. The trick that I did for this particular use case is to replace any text i find with the number 9999 and then cast the value to an integer and order by it.
for most cases that have leading characters this will get the desired result. see below
from django.db.models.expressions import RawSQL
>>> Block.objects.all()\
.annotate(my_faux_integer=RawSQL("CAST(regexp_replace(title, '[A-Z]+', '9999', 'g') AS INTEGER)", ''))\
.order_by('my_faux_integer', 'title')
<QuerySet [<Block: 1>, <Block: 2>, <Block: 10>, <Block: 15>, <Block: N1>, <Block: N4>, <Block: N12>]>
- [Django]-Session database table cleanup
- [Django]-Django model constraint for related objects
- [Django]-Django "xxxxxx Object" display customization in admin action sidebar
0
I have a similar situation. An old database has a table with mostly numeric data in a column (named page
) with type varchar
. I need to sort the results queried from that table in a natural numeric order, but I don’t need the data type changed in the results. I ended up converting the column value to a number for the sorting only:
(ItemPage.objects.filter(volume__id=volumeId)
.order_by('item__topic__name', F('page') * 1, 'item__name'))
I see some other answers here indicate that MySQL’s CAST()
may not work with strings containing a mixture of digitis and alphabetics, but from experiments, I don’t think that’s true. Maybe it’s a problem for older versions of MySQL.
Whether that’s a problem or not, I decided to multiply the value by one instead. It works well for me. At some point, I’ll experiment with CAST()
and see whether it always works and does it execute faster than multiplying by one.
- [Django]-Token Based Authentication in Django
- [Django]-Django returns 403 error when sending a POST request
- [Django]-How to serialize Django queryset.values() into json?