2011-06-01 17 views
34

Recientemente comencé a usar SQLAlchemy y todavía tengo problemas para entender algunos de los conceptos.SQLAlchemy: subconsulta en una cláusula WHERE

Reducida a los elementos esenciales, tengo dos tablas de este tipo (esto es a través matraz de SQLAlchemy):

class User(db.Model): 
    __tablename__ = 'users' 
    user_id = db.Column(db.Integer, primary_key=True) 

class Posts(db.Model): 
    __tablename__ = 'posts' 
    post_id = db.Column(db.Integer, primary_key=True) 
    user_id = db.Column(db.Integer, db.ForeignKey('users.user_id')) 
    post_time = db.Column(db.DateTime) 

    user = db.relationship('User', backref='posts') 

¿Cómo hago para consultar una lista de usuarios y su nuevo puesto (excluyendo usuarios sin publicaciones). Si yo estaba usando SQL, lo haría:

SELECT [whatever] 
FROM posts AS p 
    LEFT JOIN users AS u ON u.user_id = p.user_id 
WHERE p.post_time = (SELECT MAX(post_time) FROM posts WHERE user_id = u.user_id) 

Así que sé exactamente el SQL "deseada" para conseguir el efecto que quiero, pero no sabía cómo expresarlo "correctamente" en SQLAlchemy.

Editar: en caso de que sea importante, estoy en SQLAlchemy 0.6.6.

Respuesta

37

Esto debería funcionar (SQL diferente, mismo resultado):

t = Session.query(
    Posts.user_id, 
    func.max(Posts.post_time).label('max_post_time'), 
).group_by(Posts.user_id).subquery('t') 

query = Session.query(User, Posts).filter(and_(
    User.user_id == Posts.user_id, 
    User.user_id == t.c.user_id, 
    Posts.post_time == t.c.max_post_time, 
)) 

for user, post in query: 
    print user.user_id, post.post_id 
+5

¿Qué significa la 'C' representan en 't.c.user_id'? – Rus925

+4

'c' significa 'columnas' – 10flow

47

funciona la respuesta anterior, sino también el sql exacto que solicitó sea por escrito tanto como la declaración real:

print s.query(Posts).\ 
    outerjoin(Posts.user).\ 
    filter(Posts.post_time==\ 
     s.query(
      func.max(Posts.post_time) 
     ). 
     filter(Posts.user_id==User.user_id). 
     correlate(User). 
     as_scalar() 
    ) 

Supongo que el "concepto" que no es necesariamente aparente es que as_scalar() es actualmente necesario para establecer una subconsulta como un "escalar" (probablemente debería suponer que del contexto contra ==).

Editar: Confirmado, que es un comportamiento con errores, ticket completado # 2190. En la punta de corriente o la liberación 0.7.2, la as_scalar() es llamado automáticamente y la consulta anterior puede ser:

print s.query(Posts).\ 
    outerjoin(Posts.user).\ 
    filter(Posts.post_time==\ 
     s.query(
      func.max(Posts.post_time) 
     ). 
     filter(Posts.user_id==User.user_id). 
     correlate(User) 
    )