Django按日期排序,但最后有“无”?

时间:2011-10-13 03:40:15

标签: django django-models django-queryset

我有一个工作订单模型,其中包含需要工作订单的字段。要获得工作单列表以及早期需要的工作单,我会这样做:

wo = Work_Order.objects.order_by('dateWORequired')

这很好用,但只有在该字段中确实有值。如果没有必需的日期,则值为None。然后,工作单列表中的所有None位于顶部,然后剩余的工单按正确顺序排列。

如何在底部获取None

5 个答案:

答案 0 :(得分:38)

Django 1.11将此添加为本机功能。这有点令人费解。 It is documented

只有一个字段,升序

wo = Work_Order.objects.order_by(F('dateWORequired').asc(nulls_last=True))

使用两个字段进行排序,均为降序

wo = Work_Order.objects.order_by(F('dateWORequired').desc(nulls_last=True), F('anotherfield').desc(nulls_last=True))

答案 1 :(得分:37)

q = q.extra(select={
        'date_is_null': 'dateWORequired IS NULL',
    },
    order_by=['date_is_null','dateWORequired'],
)

你可能需要在order_by部分的date_is_null之前 - 但这就是你如何控制行为。

答案 2 :(得分:8)

当问到这个问题时,这是不可用的,但是自从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选择第一个非空值,因此您可以创建一个值date_null来订购,其中只有dateWORequired但null替换为很久以前的日期。

答案 3 :(得分:4)

要求:    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

备注

  1. 基于https://www.isotoma.com/blog/2015/11/23/sorting-querysets-with-nulls-in-django/
  2. 缺点:不必要的缓冲区字段,订购费用
  3. 变体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
    

    注意:

    1. 基于Django: Adding "NULLS LAST" to query和Django的源代码

    2. 全局模型的所有领域(同时有利有弊)

    3. 没有不必要的字段

    4. 缺点 - 仅在PostgreSQL上测试

答案 4 :(得分:0)

我努力使用纯Django,而不是放入SQL。

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调用。

我用日期尝试过,同样的事情发生了。 e.g:

SELECT 0*CURRENT_TIMESTAMP;

评估为0。