Bagaimana cara memfilter objek kueri berdasarkan rentang tanggal di Django?

248

Saya punya bidang dalam satu model seperti:

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

Sekarang, saya perlu memfilter objek berdasarkan rentang tanggal.

Bagaimana cara memfilter semua objek yang memiliki tanggal antara 1-Jan-2011dan 31-Jan-2011?

pengguna469652
sumber

Jawaban:

410

Menggunakan

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

Atau jika Anda hanya mencoba memfilter bulan bijaksana:

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

Edit

Seperti dikatakan Bernhard Vallant, jika Anda ingin queryset yang mengecualikan specified range endsAnda harus mempertimbangkan solusinya , yang menggunakan gt / lt (lebih besar dari / kurang dari).

crodjer
sumber
Apa tipe data date1? Saya punya objek datetime sekarang.
user469652
8
@dcordjer: Selain itu harus dikatakan bahwa __rangetermasuk perbatasan (seperti sql BETWEEN), jika Anda tidak ingin perbatasan termasuk Anda harus pergi dengan solusi gt / lt saya ...
Bernhard Vallant
Apakah ini secara inheren disortir dalam urutan apa pun? Jika ya, pesanan yang mana? Terima kasih.
Richard Dunn
1
@RichardDunn Pemesanan akan didasarkan pada pemesanan standar model Anda, atau jika Anda menggunakan order_bylebih dari yang dihasilkan QuerySetoleh yang disebutkan di atas filter. Saya sudah bertahun-tahun tidak menggunakan Django.
crodjer
untuk date__range Anda harus meletakkan 01 bulan berikutnya. Berikut ini tautan ke dokumen yang menerjemahkan peta yang diterjemahkan menjadi 00: 00: 00.0000 dari tanggal, oleh karena itu hari terakhir dalam rentang Anda tidak termasuk. docs.djangoproject.com/en/1.10/ref/models/querysets/#range dalam hal ini saya gunakan: date__range = ["% s-% s-1"% (tahun, bulan), "% s-% s- 1 "% (tahun, int (bulan) +1)]
SpiRail
195

Anda dapat menggunakan djangofilter dengan datetime.dateobjek :

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))
Bernhard Vallant
sumber
untuk mendapatkan semuanya termasuk hari 1 dan 31, kita harus menggunakan gte kan?
Sam Stoelinga
1
Manfaat menggunakan metode ini di atas crodjer's adalah bahwa Anda dapat memberikan objek datetime daripada string.
Brian Kung
79

Saat melakukan rentang Django dengan filter, pastikan Anda tahu perbedaan antara menggunakan objek tanggal vs objek datetime. __range termasuk pada tanggal tetapi jika Anda menggunakan objek datetime untuk tanggal akhir itu tidak akan memasukkan entri untuk hari itu jika waktu tidak ditetapkan.

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

mengembalikan semua entri dari tanggal mulai hingga tanggal termasuk entri pada tanggal tersebut. Contoh buruk karena ini mengembalikan entri seminggu ke masa depan, tetapi Anda mendapatkan penyimpangan.

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

akan hilang 24 jam senilai entri tergantung pada apa waktu untuk bidang tanggal diatur.

petugas
sumber
5
Saya pikir penting untuk mencatat cara mengimpor dateobjek: >>> from datetime import date >>> startdate = date.today()
Alex Spencer
19

Anda dapat menyiasati "ketidakcocokan impedansi" yang disebabkan oleh kurangnya presisi dalam DateTimeField/dateperbandingan objek - yang dapat terjadi jika menggunakan rentang - dengan menggunakan datetime.timedelta untuk menambahkan hari ke tanggal terakhir dalam rentang. Ini berfungsi seperti:

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])

Seperti dibahas sebelumnya, tanpa melakukan sesuatu seperti ini, catatan diabaikan pada hari terakhir.

Diedit untuk menghindari penggunaan datetime.combine- tampaknya lebih logis untuk tetap menggunakan instance date ketika membandingkan dengan DateTimeField, bukannya mengotak-atik objek yang dibuang (dan membingungkan) datetime. Lihat penjelasan lebih lanjut dalam komentar di bawah.

trojjer
sumber
1
Ada perpustakaan Delorean yang luar biasa yang menyederhanakan ini dengan metode pemotongan: delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer
@tojjer: terlihat menjanjikan, bagaimana kita menggunakan metode truncate di sini?
eugene
@ Eugene: Saya menjelajahi ini lagi sekarang, setelah semua bulan-bulan itu, dan Anda benar bahwa itu tidak benar-benar membantu dalam situasi ini. Satu-satunya cara mengatasinya yang dapat saya pikirkan adalah seperti yang disarankan dalam respons asli saya, yaitu untuk memasok 'padding' tambahan untuk perbandingan terhadap bidang model datetime ketika Anda memfilter terhadap instance tanggal. Ini dapat dilakukan melalui metode datetime.combine seperti di atas, tetapi saya telah menemukan bahwa itu bisa sedikit lebih mudah untuk hanya mengakomodasi perbedaan dengan menambahkan timedelta (hari = 1) ke tanggal mulai / berakhir di kisaran - - tergantung pada masalahnya.
trojjer
Jadi Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])tidak akan menyertakan objek yang dibuat date(2014, 2, 1), seperti yang dijelaskan oleh @cademan. Tetapi jika Anda menambah tanggal akhir dengan menambahkan satu hari, Anda akan mendapatkan queryset yang menutupi objek-objek yang hilang (dan dengan mudah menghilangkan objek yang dibuat date(2014, 2, 2)karena kekhasan yang sama). Yang menjengkelkan di sini adalah bahwa rentang 'manual' yang ditentukan dengan created__gte ... created__lte=date(2014, 2, 1)tidak berfungsi baik, yang pasti IMHO kontra-intuitif.
trojjer
1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] berfungsi untuk saya
eugene
1

Sederhana,

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

Bekerja untukku

Jonhatan Fajardo
sumber
3
Ini juga bekerja untuk saya, untuk noobs untuk kejelasan: (date__date = ...) berarti ({whateverColumnTheDateIsCalled} __ date)
Ryan Dines
2
Namun OP meminta kisaran
1919
1

Untuk membuatnya lebih fleksibel, Anda bisa mendesain FilterBackend seperti di bawah ini:

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
saran3h
sumber
-2

Masih relevan hari ini. Anda juga dapat melakukan:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Bhavik Shah
sumber