2011-10-13 37 views
33

Tôi có một mô hình đơn đặt hàng công việc, với một trường cho thời điểm yêu cầu công việc. Để có được một danh sách các lệnh làm việc, với những người đang cần sớm, tôi làm điều này:Thứ tự Django theo Ngày, nhưng có "Không" ở cuối?

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

này hoạt động độc đáo, nhưng chỉ nếu có thực sự là một giá trị trong lĩnh vực đó. Nếu không có ngày bắt buộc, thì giá trị là None. Sau đó, danh sách các đơn đặt hàng công việc có tất cả các None 's ở đầu trang, và sau đó các đơn đặt hàng làm việc còn lại theo thứ tự thích hợp.

Làm cách nào để nhận được số None ở dưới cùng?

+0

Xem http://stackoverflow.com/a/35494930/ 15690, sử dụng một lớp Query/QuerySet đặc biệt. – blueyed

Trả lời

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

Bạn có thể cần - trước ngày date_is_null trong phần order_by, nhưng đó là cách bạn có thể kiểm soát hành vi.

+0

Tuyệt vời! Làm việc như một say mê. Bây giờ tôi cần phải tìm tài liệu cho mã đó để hiểu những gì đang xảy ra. Cảm ơn dù sao đi nữa. – Garfonzo

+4

Trong khi hoạt động, nó sử dụng SQL tùy chỉnh, tốt nhất nên tránh nếu có thể.Một thay thế không sử dụng SQL tùy chỉnh được đưa ra trong câu trả lời được chấp nhận cho [Django: Thứ tự theo vị trí bỏ qua NULL] (https://stackoverflow.com/questions/5235209/django-order-by-position-ignoring-null) . – markshep

+1

'extra' không được dùng nữa và không hoạt động với bàn phím/bàn nối ngoài. – blueyed

0

Tôi cố gắng để làm việc này với Django thuần túy, mà không phải rơi vào SQL.

Hàm biểu thức F() có thể được sử dụng với order_by, vì vậy tôi cố gắng tạo một biểu thức để đặt tất cả các số thành cùng một giá trị, nhưng đặt tất cả NULL thành giá trị cụ thể khác.

MySQL sẽ đặt hàng NULL trước 0 giây theo thứ tự tăng dần và ngược lại theo thứ tự giảm dần.

Vì vậy, công trình này:

order_by((0 * F('field')).asc()) # Nulls first 
# or: 
order_by((0 * F('field')).desc()) # Nulls last 

Sau đó bạn có thể vượt qua bất kỳ lĩnh vực khác để gọi order_by cùng, trước hoặc sau khi biểu hiện đó.

Tôi đã thử nó với các ngày và điều tương tự cũng xảy ra. ví dụ:

SELECT 0*CURRENT_TIMESTAMP; 

Ước lượng để 0.

5

Đây không phải là có sẵn khi câu hỏi được hỏi, nhưng vì Django 1,8 Tôi nghĩ rằng đây là giải pháp tốt nhất:

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 chọn đầu tiên giá trị không null, do đó, bạn tạo một giá trị date_null để đặt hàng trước đó chỉ là dateWORequired nhưng với null được thay thế bằng một ngày trước.

+0

Với thực tế là '.extra()' có thể sẽ không còn được dùng nữa, đây sẽ là câu trả lời được chấp nhận. –

+1

Hoạt động với Django 1.10 - nó chỉ là 'từ django.db.models Value Value' và bạn đang thiếu và') 'sau' Value (long_ago) ' – Kroenig

4

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ú

  1. Dựa trên https://www.isotoma.com/blog/2015/11/23/sorting-querysets-with-nulls-in-django/
  2. 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ú:

  1. Dựa trên Django: Adding "NULLS LAST" to query và mã nguồn Django`s

  2. 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)

  3. Không một lĩnh vực không cần thiết

  4. Một nhược điểm - chỉ được thử nghiệm trên PostgreSQL

12

Django 1.11 thêm đây là một tính năng gốc. Đó là một chút phức tạp và không được tài liệu.

Ordered với chỉ một lĩnh vực, tăng dần:

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

Ordered sử dụng hai lĩnh vực, cả hai giảm dần:

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

Trông rất hứa hẹn, nhưng tôi không thể làm cho nó hoạt động. Có ý kiến ​​nào không? Tệp "/Users/erik/env/swida/lib/python3.5/site-packages/django/db/models/base.py", dòng 1653, trong trường = ((f [1:] nếu f.startswith ('-') else f) cho f trong các trường) AttributeError: đối tượng 'OrderBy' không có thuộc tính 'startswith' –

Các vấn đề liên quan