Yêu cầu: Python 3.4, Django 10.2, PostgreSQL 9.5.4
Variant 1
Giải pháp:
class IsNull(models.Func):
template = "%(expressions)s IS NULL"
sử dụng (Không luôn luôn mới nhất):
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
Ghi chú
- Dựa trên https://www.isotoma.com/blog/2015/11/23/sorting-querysets-with-nulls-in-django/
- Nhược điểm: Trường đệm không cần thiết, chi phí để đặt hàng
Variant 2
Giải pháp:
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)
Cách sử dụng:
# 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()
Kết quả (Không luôn luôn mới nhất):
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
Ghi chú:
Dựa trên Django: Adding "NULLS LAST" to query và mã nguồn Django`s
toàn cầu trên tất cả các lĩnh vực của một mô hình (nó là lợi thế và bất lợi đồng thời)
Không một lĩnh vực không cần thiết
Một nhược điểm - chỉ được thử nghiệm trên PostgreSQL
Xem http://stackoverflow.com/a/35494930/ 15690, sử dụng một lớp Query/QuerySet đặc biệt. – blueyed