SQLAlchemy: bagaimana cara memfilter bidang tanggal?

105

Ini modelnya:

class User(Base):
    ...
    birthday = Column(Date, index=True)   #in database it's like '1987-01-17'
    ...

Saya ingin memfilter antara dua tanggal, misalnya untuk memilih semua pengguna dalam interval 18-30 tahun.

Bagaimana cara menerapkannya dengan SQLAlchemy?

Saya berpikir tentang:

query = DBSession.query(User).filter(
    and_(User.birthday >= '1988-01-17', User.birthday <= '1985-01-17')
) 

# means age >= 24 and age <= 27

Saya tahu ini tidak benar, tetapi bagaimana melakukannya dengan benar?

Vitalii Ponomar
sumber

Jawaban:

181

Faktanya, kueri Anda benar kecuali untuk kesalahan ketik: filter Anda mengecualikan semua catatan: Anda harus mengubah <=untuk >=dan sebaliknya:

qry = DBSession.query(User).filter(
        and_(User.birthday <= '1988-01-17', User.birthday >= '1985-01-17'))
# or same:
qry = DBSession.query(User).filter(User.birthday <= '1988-01-17').\
        filter(User.birthday >= '1985-01-17')

Anda juga bisa menggunakan between:

qry = DBSession.query(User).filter(User.birthday.between('1985-01-17', '1988-01-17'))
mobil van
sumber
28
Btw, alih-alih '1985-01-17', Anda juga dapat menggunakan datetime.date(1985, 1, 17)- mungkin lebih mudah untuk digunakan atau digunakan di beberapa lingkungan.
tossbyte
5
@rippleslash: Anda benar, dan idealnya seseorang harus menggunakan tipe data yang tepat untuk parameter. Namun semua database juga memahami format ISO 8601 untuk tanggal, yang juga merupakan urutan leksikografis. Untuk alasan ini untuk contoh sederhana saya biasanya menggunakan tanggal berformat ISO - lebih mudah dibaca.
van
4
from app import SQLAlchemyDB as db

Chance.query.filter(Chance.repo_id==repo_id, 
                    Chance.status=="1", 
                    db.func.date(Chance.apply_time)<=end, 
                    db.func.date(Chance.apply_time)>=start).count()

itu sama dengan:

select
   count(id)
from
   Chance
where
   repo_id=:repo_id 
   and status='1'
   and date(apple_time) <= end
   and date(apple_time) >= start

berharap dapat membantu Anda.

sebuah alat
sumber
3

jika Anda ingin mendapatkan seluruh periode:

    from sqlalchemy import and_, func

    query = DBSession.query(User).filter(and_(func.date(User.birthday) >= '1985-01-17'),\
                                              func.date(User.birthday) <= '1988-01-17'))

Itu berarti kisaran: 1985-01-17 00:00 - 1988-01-17 23:59

MrNinjamannn
sumber
BAHAYA: meskipun ini mungkin terlihat jelas bagi sebagian orang - ini HANYA berfungsi karena func.dateapakah CAST pada kolom yang menghapus waktu dari persamaan => ini BUKAN berarti rentang dengan waktu! Ini hanya berfungsi ketika waktu TIDAK ada dalam kolom - Anda harus MENGHANCURKANnya ke Tanggal seperti ini, atau membuat kolom Tanggal, setelah itu DateTime atau cap waktu - biasanya selesai dengan 00:00 (baik MySQL & PostgreSQL melakukan ini). Solusi yang lebih umum adalah tidak melakukan cast, tetapi untuk mengatur tanggal pengiriman ke .endOfDay () sehingga Anda benar-benar mengirim 1988-01-17 23:59:59ke database untuk membandingkan :)
jave.web