作業指示のモデルがあり、作業指示がいつ必要になるかについてのフィールドがあります。作業指示のリストを取得するには、早期に必要なものを使用して、次のようにします。
wo = Work_Order.objects.order_by('dateWORequired')
これはうまく機能しますが、実際にそのフィールドに値がある場合のみです。必要な日付がない場合、値はNone
です。次に、作業オーダーのリストの先頭にすべてのNone
があり、残りの作業オーダーは適切な順序になっています。
None
を一番下に表示するにはどうすればよいですか?
q = q.extra(select={
'date_is_null': 'dateWORequired IS NULL',
},
order_by=['date_is_null','dateWORequired'],
)
Order_by部分のdate_is_nullの前に-が必要になる場合がありますが、これにより動作を制御できます。
Django 1.11はこれをネイティブ機能として追加しました。少し複雑です。 ドキュメント化されています 。
1つのフィールドのみで昇順で並べ替え、:
wo = Work_Order.objects.order_by(F('dateWORequired').asc(nulls_last=True))
2つのフィールドを使用して順序付け、どちらも降順:
wo = Work_Order.objects.order_by(F('dateWORequired').desc(nulls_last=True), F('anotherfield').desc(nulls_last=True))
これは質問があったときに利用できませんでしたが、Django= 1.8なので、これが最良の解決策だと思います:
from Django.db.models import Coalesce, Value
long_ago = datetime.datetime(year=1980, month=1, day=1)
Work_Order.objects.order_by('dateWORequired')
MyModel.objects.annotate(date_null=
Coalesce('dateWORequired', Value(long_ago))).order_by('date_null')
Coalesce
は最初のnull以外の値を選択するため、date_OORequiredであるdate_null
を作成して値を作成しますが、null
はずっと前の日付に置き換えられます。
要件:Python 3.4、Django 10.2、PostgreSQL 9.5.4
バリアント1
ソリューション:
class IsNull(models.Func):
template = "%(expressions)s IS NULL"
使用法(常に最新ではない):
In [1]: a = User.polls_manager.users_as_voters()
In [4]: from Django.db import models
In [5]: class IsNull(models.Func):
...: template = "%(expressions)s IS NULL"
...:
In [7]: a = a.annotate(date_latest_voting_isnull=IsNull('date_latest_voting'))
In [9]: for i in a.order_by('date_latest_voting_isnull', 'date_latest_voting'):
...: print(i.date_latest_voting)
...:
2016-07-30 01:48:11.872911+00:00
2016-08-31 13:13:47.240085+00:00
2016-09-16 00:04:23.042142+00:00
2016-09-18 19:45:54.958573+00:00
2016-09-26 07:27:34.301295+00:00
2016-10-03 14:01:08.377417+00:00
2016-10-21 16:07:42.881526+00:00
2016-10-23 11:10:02.342791+00:00
2016-10-31 04:09:03.726765+00:00
None
In [10]: for i in a.order_by('date_latest_voting_isnull', '-date_latest_voting'):
...: print(i.date_latest_voting)
...:
2016-10-31 04:09:03.726765+00:00
2016-10-23 11:10:02.342791+00:00
2016-10-21 16:07:42.881526+00:00
2016-10-03 14:01:08.377417+00:00
2016-09-26 07:27:34.301295+00:00
2016-09-18 19:45:54.958573+00:00
2016-09-16 00:04:23.042142+00:00
2016-08-31 13:13:47.240085+00:00
2016-07-30 01:48:11.872911+00:00
None
注意事項
バリアント2
ソリューション:
from Django.db import models
from Django.db import connections
from Django.db.models.sql.compiler import SQLCompiler
class NullsLastCompiler(SQLCompiler):
# source code https://github.com/Django/django/blob/master/Django/db/models/sql/compiler.py
def get_order_by(self):
result = super(NullsLastCompiler, self).get_order_by()
# if result exists and backend is PostgreSQl
if result and self.connection.vendor == 'postgresql':
# modified raw SQL code to ending on NULLS LAST after ORDER BY
# more info https://www.postgresql.org/docs/9.5/static/queries-order.html
result = [
(expression, (sql + ' NULLS LAST', params, is_ref))
for expression, (sql, params, is_ref) in result
]
return result
class NullsLastQuery(models.sql.Query):
# source code https://github.com/Django/django/blob/master/Django/db/models/sql/query.py
def get_compiler(self, using=None, connection=None):
if using is None and connection is None:
raise ValueError("Need either using or connection")
if using:
connection = connections[using]
# return own compiler
return NullsLastCompiler(self, connection, using)
class NullsLastQuerySet(models.QuerySet):
# source code https://github.com/Django/django/blob/master/Django/db/models/query.py
def __init__(self, model=None, query=None, using=None, hints=None):
super(NullsLastQuerySet, self).__init__(model, query, using, hints)
# replace on own Query
self.query = query or NullsLastQuery(model)
用途:
# instead of models.QuerySet use NullsLastQuerySet
class UserQuestionQuerySet(NullsLastQuerySet):
def users_with_date_latest_question(self):
return self.annotate(date_latest_question=models.Max('questions__created'))
#connect to a model as a manager
class User(AbstractBaseUser, PermissionsMixin):
.....
questions_manager = UserQuestionQuerySet().as_manager()
結果(常に最新ではない):
In [2]: qs = User.questions_manager.users_with_date_latest_question()
In [3]: for i in qs:
...: print(i.date_latest_question)
...:
None
None
None
2016-10-28 20:48:49.005593+00:00
2016-10-04 19:01:38.820993+00:00
2016-09-26 00:35:07.839646+00:00
None
2016-07-27 04:33:58.508083+00:00
2016-09-14 10:40:44.660677+00:00
None
In [4]: for i in qs.order_by('date_latest_question'):
...: print(i.date_latest_question)
...:
2016-07-27 04:33:58.508083+00:00
2016-09-14 10:40:44.660677+00:00
2016-09-26 00:35:07.839646+00:00
2016-10-04 19:01:38.820993+00:00
2016-10-28 20:48:49.005593+00:00
None
None
None
None
None
In [5]: for i in qs.order_by('-date_latest_question'):
...: print(i.date_latest_question)
...:
2016-10-28 20:48:49.005593+00:00
2016-10-04 19:01:38.820993+00:00
2016-09-26 00:35:07.839646+00:00
2016-09-14 10:40:44.660677+00:00
2016-07-27 04:33:58.508083+00:00
None
None
None
None
None
注:
Django:クエリに「NULLS LAST」を追加 とDjangoのソースコードに基づく
モデルのすべてのフィールドでグローバル(それは同時に利点と欠点です)
不要なフィールドはありません
欠点-PostgreSQLでのみテスト済み
私はSQLに落ちることなく、これを純粋なDjangoで動作させるように努めました。
F()式関数はorder_byで使用できるため、すべての数値を同じ値に設定するが、すべてのNULLを別の特定の値に設定する式を作成する方法を作成しようとしました。
MySQLは、0の前にNULLを昇順で、またはその逆を降順に並べます。
したがって、これは機能します:
order_by( (0 * F('field')).asc() ) # Nulls first
# or:
order_by( (0 * F('field')).desc() ) # Nulls last
次に、その式の前または後に、他のフィールドを同じorder_by呼び出しに渡すことができます。
私は日付でそれを試しました、そして同じことが起こります。例えば。:
SELECT 0*CURRENT_TIMESTAMP;
0と評価されます。