SQLAlchemy: jak filtrować pole daty?

105

Oto model:

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

Chcę filtrować między dwiema datami, na przykład, aby wybrać wszystkich użytkowników w przedziale 18-30 lat.

Jak zaimplementować to z SQLAlchemy?

Myślę o:

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

# means age >= 24 and age <= 27

Wiem, że to nie jest poprawne, ale jak to zrobić?

Vitalii Ponomar
źródło

Odpowiedzi:

181

W rzeczywistości twoje zapytanie jest poprawne, z wyjątkiem literówki: twój filtr wyklucza wszystkie rekordy: powinieneś zmienić <=for >=i odwrotnie:

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

Możesz również użyć between:

qry = DBSession.query(User).filter(User.birthday.between('1985-01-17', '1988-01-17'))
awangarda
źródło
28
Przy okazji, zamiast tego '1985-01-17'możesz również użyć datetime.date(1985, 1, 17)- może być łatwiejszy do uzyskania lub pracy w niektórych środowiskach.
tossbyte
5
@rippleslash: masz rację i najlepiej byłoby użyć odpowiedniego typu danych dla parametrów. Jednak wszystkie bazy danych rozumieją również format dat ISO 8601, który jest również porządkiem leksykograficznym. Z tego powodu w prostych przykładach zwykle używam dat w formacie ISO - łatwiejszych do odczytania.
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()

jest równa:

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

życzenie może ci pomóc.

narzędzie
źródło
3

jeśli chcesz otrzymać cały okres:

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

Oznacza to zakres: 1985-01-17 00:00 - 1988-01-17 23:59

MrNinjamannn
źródło
NIEBEZPIECZEŃSTWO: chociaż dla niektórych może to być oczywiste - to działa TYLKO, ponieważ func.daterobi CAST na kolumnie, co usuwa czas z równania => to działa NIE oznacza to zakresu z czasem! Działa to tylko wtedy, gdy czas NIE jest w kolumnie - musisz CAST go na Date w ten sposób lub ustawić kolumnę Date, gdy jest to DateTime lub timestamp - zwykle kończy się na 00:00 (robią to zarówno MySQL, jak i PostgreSQL). Bardziej ogólnym rozwiązaniem nie jest przesyłanie, ale ustawienie daty wysyłania do .endOfDay (), aby faktycznie wysłać 1988-01-17 23:59:59do bazy danych porównanie :)
jave.web