SQLAlchemy: ¿cómo filtrar el campo de fecha?

105

Aquí está el modelo:

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

Quiero filtrar entre dos fechas, por ejemplo, para elegir a todos los usuarios en un intervalo de 18 a 30 años.

¿Cómo implementarlo con SQLAlchemy?

Pienso:

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

# means age >= 24 and age <= 27

Sé que esto no es correcto, pero ¿cómo hacerlo?

Vitalii Ponomar
fuente

Respuestas:

181

De hecho, su consulta es correcta excepto por el error tipográfico: su filtro excluye todos los registros: debe cambiar el <=for >=y viceversa:

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

También puedes usar between:

qry = DBSession.query(User).filter(User.birthday.between('1985-01-17', '1988-01-17'))
camioneta
fuente
28
Por cierto, en lugar de '1985-01-17', también puede usar datetime.date(1985, 1, 17): puede ser más fácil de acceder o trabajar en algunos entornos.
tossbyte
5
@rippleslash: tiene razón, e idealmente uno debería usar el tipo de datos adecuado para los parámetros. Sin embargo, todas las bases de datos también entienden el formato ISO 8601 para fechas, que también es un orden lexicográfico. Por esta razón, para ejemplos simples, generalmente uso fechas en formato ISO, más fáciles de leer.
furgoneta
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()

es igual a:

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

deseo puede ayudarte.

una herramienta
fuente
3

si desea obtener todo el período:

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

Eso significa rango: 1985-01-17 00:00 - 1988-01-17 23:59

MrNinjamannn
fuente
PELIGRO: aunque esto puede ser obvio para algunos, esto SOLO funciona porque func.datehace CAST en la columna que elimina el tiempo de la ecuación => ¡esto NO significa rango con el tiempo! Esto solo funciona cuando el tiempo NO está en la columna; debe CASTARlo a la fecha de esta manera, o hacer que la columna sea Fecha, una vez que sea DateTime o marca de tiempo; generalmente termina con 00:00 (tanto MySQL como PostgreSQL hacen esto). Una solución más genérica no es emitir, sino establecer la fecha en la que está enviando su .endOfDay () para que realmente envíe 1988-01-17 23:59:59a la base de datos compare :)
jave.web