Bagaimana saya bisa memfilter tanggal DateTimeField di Django?

173

Saya mencoba memfilter DateTimeFieldperbandingan dengan tanggal. Maksudku:

MyObject.objects.filter(datetime_attr=datetime.date(2009,8,22))

Saya mendapatkan daftar queryset kosong sebagai jawaban karena (saya pikir) saya tidak mempertimbangkan waktu, tetapi saya ingin "kapan saja".

Apakah ada cara mudah di Django untuk melakukan ini?

Saya punya waktu di datetime diselesaikan, tidak 00:00.

Xidobix
sumber
5
Ini adalah salah satu gangguan dari Django. Mengingat ini adalah kasus penggunaan yang sederhana dan umum, tidak ada cara sederhana untuk mencapai ini.
rubayeet

Jawaban:

114

Pencarian tersebut diimplementasikan django.views.generic.date_basedsebagai berikut:

{'date_time_field__range': (datetime.datetime.combine(date, datetime.time.min),
                            datetime.datetime.combine(date, datetime.time.max))} 

Karena cukup verbose ada rencana untuk meningkatkan sintaks menggunakan __dateoperator. Periksa " # 9596 Membandingkan DateTimeField ke tanggal terlalu sulit " untuk detail lebih lanjut.

Piotr Czapla
sumber
4
Menggunakan dengan rentang: Q(created__gte=datetime.combine(created_value, time.min))
Dingo
10
Sepertinya akan mendarat di Django 1.9: github.com/django/django/commit/…
amjoconn
14
Baru di Django 1.9:Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Non
102
YourModel.objects.filter(datetime_published__year='2008', 
                         datetime_published__month='03', 
                         datetime_published__day='27')

// edit setelah komentar

YourModel.objects.filter(datetime_published=datetime(2008, 03, 27))

doest tidak berfungsi karena ia membuat objek datetime dengan nilai waktu yang ditetapkan ke 0, sehingga waktu dalam database tidak cocok.

Zewew
sumber
Terima kasih atas jawabannya! alternatif pertama tidak bekerja dengan datetimefields. Alternatif kedua bekerja;). Jika seseorang tahu metode lain, silakan jawab
Xidobix
docs.python.org/library/datetime.html#datetime-objects menggunakan datetime () dari modul datetime jam, menit, dtk adalah opsional. yang kedua adalah dari proyek yang bekerja dengan vars diganti, Anda dapat melihat di docs itu benar
Zalew
saya tahu itu adalah opsional, masalahnya adalah bahwa datetimefield saya memiliki waktu setted, tidak 00:00
Xidobix
"Alternatif pertama tidak bekerja dengan datetimefields." itu akan sangat mengejutkan, karena datetime.datetime () mengembalikan objek datetime djangoproject.com/documentation/0.96/models/basic memeriksa definisi model dan contoh: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew
"Aku tahu itu opsional, masalahnya adalah datetimefield-ku punya waktu, ini bukan 00:00" Oh, sekarang aku mengerti. Ya, tanpa argumen waktu itu set ke 00, sehingga tidak kembali :)
Zalew
88

Berikut adalah hasil yang saya dapatkan dengan fungsi timeit ipython:

from datetime import date
today = date.today()

timeit[Model.objects.filter(date_created__year=today.year, date_created__month=today.month, date_created__day=today.day)]
1000 loops, best of 3: 652 us per loop

timeit[Model.objects.filter(date_created__gte=today)]
1000 loops, best of 3: 631 us per loop

timeit[Model.objects.filter(date_created__startswith=today)]
1000 loops, best of 3: 541 us per loop

timeit[Model.objects.filter(date_created__contains=today)]
1000 loops, best of 3: 536 us per loop

mengandung sepertinya lebih cepat.

Moreno
sumber
Solusi ini tampaknya menjadi yang terbaru. Saya terkejut mendapat 4 upvotes, karena ketika saya mencoba containssolusinya, saya mendapatkan pesan kesalahan:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman
Saya periksa ulang dan perbarui hasilnya hari ini dan saya pikir kesalahan Anda bukan disebabkan oleh __containsfilter. Tetapi jika Anda mengalami masalah Anda harus mencoba contoh dokumen Django yang menggunakan __gte.
Moreno
4
Metode __contains berfungsi dengan baik untuk saya. Saya pikir ini mungkin jawaban terbaik karena memberikan perbandingan kinerja. Saya telah memilih lebih dari satu, tetapi saya terkejut itu tidak memiliki lebih banyak suara.
RobotHumans
Saya suka startswithsolusinya ..
w411 3
46

Sekarang Django memiliki filter queryset __date untuk menanyakan objek datetime terhadap tanggal dalam versi pengembangan. Dengan demikian, itu akan tersedia dalam 1,9 segera.

onurmatik
sumber
Yup, itu ditambahkan dalam 1.9 docs.djangoproject.com/en/1.9/ref/models/querysets/#date
guival
Jawaban terbaik, valid untuk versi Django yang lebih baru
serfer2
Ini tidak bekerja untuk saya, tidak tahu mengapa :( Saya menggunakan Django 1.11 Pengecualian saya yang tepat adalah: NotImplementedError: subkelas operasi basis data mungkin memerlukan metode datetime_cast_date ()
AbdurRehman Khan
44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

di atas adalah apa yang saya gunakan. Tidak hanya berfungsi, itu juga memiliki beberapa dukungan logis yang melekat.

kettlehell
sumber
3
Jauh lebih baik daripada semua jawaban lain di sini, terima kasih!
Kin
sssss-shazam! 💯
dps
30

Pada Django 1.9, cara untuk melakukan ini adalah dengan menggunakan __dateobjek datetime.

Sebagai contoh: MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))

Andrew B.
sumber
27

Ini menghasilkan hasil yang sama seperti menggunakan __year, __month, dan __day dan sepertinya bekerja untuk saya:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
mhost
sumber
19
Sepertinya ini mengubah objek tanggal menjadi string dan melakukan perbandingan string tanggal karenanya memaksa db untuk melakukan pemindaian tabel penuh. untuk tabel besar yang satu ini membunuh kinerja Anda
yilmazhuseyin
8

dengan asumsi active_on adalah objek tanggal, tambahkan 1 hari kemudian lakukan rentang

next_day = active_on + datetime.timedelta(1)
queryset = queryset.filter(date_created__range=(active_on, next_day) )
davidj411
sumber
2

Berikut adalah teknik yang menarik - Saya memanfaatkan prosedur startswith seperti yang diterapkan dengan Django pada MySQL untuk mencapai hasil hanya mencari datetime hanya melalui tanggal. Pada dasarnya, ketika Django melakukan pencarian dalam database, ia harus melakukan konversi string untuk objek penyimpanan DATETIME MySQL, sehingga Anda dapat memfilternya, meninggalkan bagian stempel waktu tanggal - dengan cara itu% LIKE% hanya cocok dengan tanggal objek dan Anda akan mendapatkan setiap cap waktu untuk tanggal yang diberikan.

datetime_filter = datetime(2009, 8, 22) 
MyObject.objects.filter(datetime_attr__startswith=datetime_filter.date())

Ini akan melakukan kueri berikut:

SELECT (values) FROM myapp_my_object \ 
WHERE myapp_my_object.datetime_attr LIKE BINARY 2009-08-22%

LIKE BINARY dalam hal ini akan cocok dengan segalanya untuk tanggal, tidak peduli cap waktu. Termasuk nilai-nilai seperti:

+---------------------+
| datetime_attr       |
+---------------------+
| 2009-08-22 11:05:08 |
+---------------------+

Semoga ini membantu semua orang sampai Django keluar dengan solusi!

bbengfort
sumber
Ok, jadi ini tampaknya jawaban yang sama dengan mhost dan kettlehell di atas, tetapi dengan deskripsi lebih lanjut tentang apa yang terjadi di backend. Setidaknya Anda memiliki alasan untuk menggunakan berisi atau memulai bersama dengan atribut date () dari datetime!
bbengfort
2

Ada blogpost fantastis yang membahas hal ini di sini: Membandingkan Tanggal dan Waktu di Django ORM

Solusi terbaik untuk Django> 1.7, <1.9 adalah dengan mendaftarkan transformasi:

from django.db import models

class MySQLDatetimeDate(models.Transform):
    """
    This implements a custom SQL lookup when using `__date` with datetimes.
    To enable filtering on datetimes that fall on a given date, import
    this transform and register it with the DateTimeField.
    """
    lookup_name = 'date'

    def as_sql(self, compiler, connection):
        lhs, params = compiler.compile(self.lhs)
        return 'DATE({})'.format(lhs), params

    @property
    def output_field(self):
        return models.DateField()

Kemudian Anda dapat menggunakannya di filter Anda seperti ini:

Foo.objects.filter(created_on__date=date)

EDIT

Solusi ini pasti tergantung ujung belakang. Dari artikel:

Tentu saja, implementasi ini bergantung pada cita rasa khusus SQL Anda yang memiliki fungsi DATE (). MySQL melakukannya. Begitu juga SQLite. Di sisi lain, saya belum bekerja dengan PostgreSQL secara pribadi, tetapi beberapa googling membuat saya percaya bahwa ia tidak memiliki fungsi DATE (). Jadi implementasi yang sederhana ini sepertinya akan selalu tergantung pada backend.

Dan Gayle
sumber
Apakah ini spesifik MySQL? bertanya-tanya tentang pilihan nama kelas.
Binoj David
@BinojDavid Ya, itu tergantung backend
Dan Gayle
Saya mengujinya menggunakan Django 1.8 dan PostgreSQL 9.6.6 dan bekerja dengan baik. Sebenarnya menggunakan PostgreSQL Anda juga dapat menggunakan sintaks ini:return '{}::date'.format(lhs), params
michal-michalak
1

Hm .. Solusi saya berfungsi:

Mymodel.objects.filter(date_time_field__startswith=datetime.datetime(1986, 7, 28))
satelit
sumber
1
Model.objects.filter(datetime__year=2011, datetime__month=2, datetime__day=30)
Skylar Saveland
sumber
0

Dalam Django 1.7.6 karya:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
sumber
2
Ini hanya berfungsi jika Anda mencari tanggal yang tepat, Anda tidak dapat menggunakan __ltemisalnya.
Persaingan
-1

Lihat artikel Dokumentasi Django

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
Shahjapan
sumber
5
dalam dokumentasi Django itu berfungsi karena datetimefile punya waktu 00:00
Xidobix