I have the following model that I need to order by the grade
attribute, but that attribute can be either a character or integer (K, KA, KF, NA, 1, 2, 3, etc.)... what is the best way to accomplish that? We are using MS SQL as a backend (I cannot use __regex
because the DB version does not support it).
I annotated a first_letter_group
attribute onto the queryset because any grade with multiple letters should be treated as it's first letter (KA = K, KN = K, etc.)
I'm trying to order these results by alpha first, then ascending numerically (e.g. 'P, K, 1, 2, 3, 4...')
# This is an unmanaged model
class Enrollment(models.Model):
id = models.IntegerField(db_column="ID", primary_key=True)
location_id = models.IntegerField(db_column="loc_id")
grade = models.CharField(db_column="grade", max_length=10)
end_year = models.IntegerField(db_column="end_year")
run_date = models.DateField(db_column="run_date")
student_count = models.IntegerField(db_column="students")
I've tried annotating additional fields where I cast the results to CharField
or IntegerField
but they (expectedly) error out when they hit something that does not convert.
all_enrollments = (
Enrollment.objects.filter(
location_id__in=location_ids,
end_year=today.year,
)
.order_by("-run_date")
.annotate(first_letter_group=Substr("grade", 1, 1))
)
ordered_enrollment_list = (
all_enrollments.annotate(
number_field=Cast(
"first_letter_group", output_field=IntegerField()
),
str_field=Cast(
"first_letter_group", output_field=IntegerField()
),
)
.order_by("-str_field", "number_field")
)
I can't figure out how to do this without the __regex
functionality which our DB does not support.
CodePudding user response:
It's not pretty, but as far as i have understood your question it does the job
numbers = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
ordered_enrollment_list = (
all_enrollments.annotate(
number_field=Case(
When(first_letter_group__in=numbers, then=Cast(F('first_letter_group'), IntegerField())),
default=Value(0),
output_field=IntegerField()
),
str_field=Case(
When(first_letter_group__in=numbers, then=Value(False)),
default=Cast(F('first_letter_group'), CharField()),
output_field=CharField()
),
)
.order_by('-str_field', 'number_field')
)