Làm cách nào để lọc các đối tượng truy vấn theo phạm vi ngày trong Django?


248

Tôi đã có một lĩnh vực trong một mô hình như:

class Sample(models.Model):
    date = fields.DateField(auto_now=False)

Bây giờ, tôi cần lọc các đối tượng theo một phạm vi ngày.

Làm cách nào để lọc tất cả các đối tượng có ngày giữa 1-Jan-201131-Jan-2011?

Câu trả lời:


409

Sử dụng

Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])

Hoặc nếu bạn chỉ cố gắng lọc tháng khôn ngoan:

Sample.objects.filter(date__year='2011', 
                      date__month='01')

Biên tập

Như Bernhard Vallant đã nói, nếu bạn muốn có một bộ truy vấn loại trừ specified range endsbạn nên xem xét giải pháp của anh ta , sử dụng gt / lt (lớn hơn / ít hơn).


Kiểu dữ liệu của date1 là gì? Tôi đã có đối tượng datetime bây giờ.
dùng469652

8
@dcordjer: Phải nói một cách bổ sung __rangebao gồm các đường viền (như sql BETWEEN), nếu bạn không muốn các đường viền bao gồm, bạn sẽ phải đi với giải pháp gt / lt của tôi ...
Bernhard Vallant

Đây có phải là sắp xếp theo thứ tự? Nếu vậy, thứ tự nào? Cảm ơn.
Richard Dunn

1
@RichardDunn Việc đặt hàng sẽ dựa trên thứ tự mặc định của mô hình của bạn hoặc nếu bạn sử dụng order_bytrên mức được tạo QuerySetbởi các đề cập ở trên filter. Tôi đã không sử dụng Django trong nhiều năm.
crodjer

cho date__range bạn cần đặt 01 của tháng tiếp theo. Đây là một liên kết đến tài liệu exmaplins mà nó dịch thành 00: 00: 00.0000 của ngày, do đó không bao gồm ngày cuối cùng trong phạm vi của bạn. docs.djangoproject.com/en/1.10/ref/models/querysets/#range trong trường hợp này tôi sử dụng: date__range = ["% s-% s-1"% (năm, tháng), "% s-% s- 1 "% (năm, int (tháng) +1)]
SpiRail

195

Bạn có thể sử dụng djangofilter với datetime.datecác đối tượng :

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))

để có được mọi thứ kể cả ngày 1 và 31, chúng ta sẽ phải sử dụng gte phải không?
Sam Stoelinga

1
Một lợi ích của việc sử dụng phương thức này trên crodjer là bạn có thể truyền cho nó các đối tượng datetime thay vì chuỗi.
Brian Kung

79

Khi thực hiện phạm vi django với bộ lọc, hãy đảm bảo bạn biết sự khác biệt giữa việc sử dụng đối tượng ngày so với đối tượng datetime. __range được bao gồm vào các ngày nhưng nếu bạn sử dụng một đối tượng datetime cho ngày kết thúc thì nó sẽ không bao gồm các mục cho ngày đó nếu thời gian không được đặt.

    startdate = date.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

trả về tất cả các mục từ startdate đến enddate bao gồm các mục vào những ngày đó. Ví dụ tồi vì điều này sẽ trả lại các mục một tuần trong tương lai, nhưng bạn có được sự trôi dạt.

    startdate = datetime.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

sẽ bị thiếu các mục nhập trong 24 giờ tùy thuộc vào thời gian cho các trường ngày được đặt thành.


5
Tôi nghĩ điều quan trọng cần lưu ý là làm thế nào để nhập một dateđối tượng: >>> from datetime import date >>> startdate = date.today()
Alex Spencer

19

Bạn có thể vượt qua "sự không phù hợp trở kháng" do thiếu độ chính xác trong DateTimeField/dateso sánh đối tượng - điều đó có thể xảy ra nếu sử dụng phạm vi - bằng cách sử dụng datetime.timedelta để thêm một ngày vào ngày cuối cùng trong phạm vi. Điều này hoạt động như:

start = date(2012, 12, 11)
end = date(2012, 12, 18)
new_end = end + datetime.timedelta(days=1)

ExampleModel.objects.filter(some_datetime_field__range=[start, new_end])

Như đã thảo luận trước đây, không làm điều gì đó như thế này, hồ sơ sẽ bị bỏ qua vào ngày cuối cùng.

Được chỉnh sửa để tránh việc sử dụng datetime.combine- có vẻ hợp lý hơn để gắn bó với các trường hợp ngày khi so sánh với a DateTimeField, thay vì gây rối với datetimecác đối tượng vứt bỏ (và khó hiểu) . Xem giải thích thêm trong ý kiến ​​dưới đây.


1
Có một thư viện DeLorean tuyệt vời mà đơn giản hoá này với một phương pháp cắt ngắn: delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer

@tojjer: có vẻ đầy hứa hẹn, làm thế nào để chúng ta sử dụng phương pháp cắt ngắn ở đây?
eugene

@eugene: Tôi đã khám phá điều này một lần nữa, sau tất cả những tháng đó, và bạn nói đúng rằng nó không thực sự giúp ích trong tình huống này. Cách duy nhất mà tôi có thể nghĩ đến là như được đề xuất trong phản hồi ban đầu của tôi, đó là cung cấp thêm 'phần đệm' để so sánh với trường mô hình datetime khi bạn lọc theo trường hợp ngày. Điều này có thể được thực hiện thông qua phương thức datetime.combine như trên, nhưng tôi thấy rằng có thể đơn giản hơn một chút để chỉ điều chỉnh sự khác biệt bằng cách thêm một timedelta (ngày = 1) vào ngày bắt đầu / ngày kết thúc trong phạm vi - - tùy thuộc vào vấn đề.
trojjer

Vì vậy, Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])sẽ không bao gồm các đối tượng được tạo trên date(2014, 2, 1), như @cademan giải thích hữu ích. Nhưng nếu bạn tăng ngày kết thúc bằng cách thêm một ngày, bạn sẽ nhận được một bộ truy vấn bao gồm các đối tượng bị thiếu đó (và thuận tiện bỏ qua các đối tượng được tạo date(2014, 2, 2)vì cùng một cách giải quyết). Điều khó chịu ở đây là một phạm vi 'thủ công' được chỉ định created__gte ... created__lte=date(2014, 2, 1)không hoạt động, điều này chắc chắn là IMHO phản trực giác.
trojjer

1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014 / 02-01'). date] hoạt động với tôi
eugene

1

Thì đơn giản,

YourModel.objects.filter(YOUR_DATE_FIELD__date=timezone.now())

Làm việc cho tôi


3
Điều này làm việc cho tôi là tốt, cho noobs cho rõ ràng: (date__date = ...) nghĩa là ({} whateverColumnTheDateIsCalled __ ngày)
Ryan Dines

2
OP đã yêu cầu một phạm vi tuy nhiên
aliasav

1

Để làm cho nó linh hoạt hơn, bạn có thể thiết kế FilterBackend như sau:

class AnalyticsFilterBackend(generic_filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        predicate = request.query_params # or request.data for POST

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is not None:
            queryset = queryset.filter(your_date__range=(predicate['from_date'], predicate['to_date']))

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is None:
            queryset = queryset.filter(your_date__gte=predicate['from_date'])

        if predicate.get('to_date', None) is not None and predicate.get('from_date', None) is None:
            queryset = queryset.filter(your_date__lte=predicate['to_date'])
        return queryset

-2

Vẫn còn liên quan đến ngày hôm nay. Bạn cũng có thể làm:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.