##経緯
一度DjangoQuerySet内で取得した要素をしっかりオーダーできたら便利だ。
だが、テーブルのpriorityカラムはNULL値も受け入れていて、普通にorder_by('priority')とかやってしまうと、NULLのものが一番先頭に来てしまう。一方で、仕様ではpriority=Noneは劣後ということになっている。
したがって、今までは、DQSで取得した後でsorted+lambdaを使って整列していた。
これを、やはりDQSで一気に実現してしまいたい…。
##解決法
DQSでextraを使う。
extraのselectを使うと、selectの結果を新しく命名した内容であたり外れ判定してくれる
# example
target_features = list(MFeature.objects.using('front')
.select_related()
.filter(is_active=Common.IS_ACTIVE_ON,
end_dt__gte=now,
start_dt__lte=now,
mfeaturetalent__is_active=Common.IS_ACTIVE_ON,
mfeaturetalent__talent__is_active=Common.IS_ACTIVE_ON)
.values('id', 'title', 'detail', 'link_url', 'priority', 'start_dt',
'mfeaturetalent__title', 'mfeaturetalent__footnote',
'mfeaturetalent__link_url', 'mfeaturetalent__image_url',
'mfeaturetalent__talent__id', 'mfeaturetalent__talent__name',
'mfeaturetalent__talent__photo_ext', 'mfeaturetalent__rank')
.extra(select={'priority_is_null': "m_feature.priority IS NULL"})
.order_by('priority_is_null', 'priority', '-start_dt', 'mfeaturetalent__rank'))
ここではextraでpriority_is_nullという定義名にm_feature.priority IS NULL
の判定が入る。そこで、それをorder_byの中に渡してやると、priorityのNULL判定が機能する。
###注意点
extra(select={})の中で、m_feature.priority
とせずに、priority
とだけにすると、Django column 'priority' in order clause is ambiguous
というmysqlの警告が出る。
これは、上記のDQLでm_feature_talentをinner joinしているため、どのテーブルのpriorityかが明確じゃないことから起こるエラー。
##参考URL
Django Order By Date, but have “None” at end?
QuerySet API リファレンス#extra