Tu HAVING
se maneja correctamente, pero le está pasando la expresión incorrecta. Parece que estás usando Python 2, ya que la comparación relacional entre una cadena y un entero
'distance' < 25
no genera una excepción, pero se evalúa como False
en cambio. En otras palabras, su consulta es igual a
locations = db.session.query(...).having(False).all()
lo que explica por qué obtiene cero resultados:todas las filas se filtran explícitamente mediante la cláusula HAVING, como se ve en la versión impresa:
...
HAVING false = 1 -- remove all rows
Una solución es utilizar una construcción adecuada, como column()
, para producir la expresión:
locations = db.session.query(...).having(column('distance') < 25).all()
No debe envolver la expresión de elemento de lista de selección compleja en una select()
, que representa una instrucción SELECT. Etiquete el text()
como es:
text('( 6371 * acos( cos( radians("53.6209798282177") ) * '
'cos( radians( lat ) ) * cos( radians( lng ) - radians("13.96948162900808") ) + '
'sin( radians("53.6209798282177") ) * sin( radians( lat ) ) ) ) '
'AS distance')
o construye la expresión usando el modelo:
(6371 *
func.acos(func.cos(func.radians(53.6209798282177)) *
func.cos(func.radians(Location.lat)) *
func.cos(func.radians(Location.lng) - func.radians(13.96948162900808)) +
func.sin(func.radians(53.6209798282177)) *
func.sin(func.radians(Location.lat)))).label('distance')
Podría mejorar la legibilidad de la construcción de su consulta creando una función para la gran distancia del círculo , y con un poco de trabajo podría implementar un método híbrido
en Location
:
import math
def gc_distance(lat1, lng1, lat2, lng2, math=math):
ang = math.acos(math.cos(math.radians(lat1)) *
math.cos(math.radians(lat2)) *
math.cos(math.radians(lng2) -
math.radians(lng1)) +
math.sin(math.radians(lat1)) *
math.sin(math.radians(lat2)))
return 6371 * ang
class Location(db.Model):
...
@hybrid_method
def distance(self, lat, lng):
return gc_distance(lat, lng, self.lat, self.lng)
@distance.expression
def distance(cls, lat, lng):
return gc_distance(lat, lng, cls.lat, cls.lng, math=func)
locations = db.session.query(
Location,
Location.distance(53.6209798282177,
13.96948162900808).label('distance')).\
having(column('distance') < 25).\
order_by('distance').\
all()
Tenga en cuenta que la forma en que usa HAVING para eliminar filas que no son de grupo no es portátil. Por ejemplo, en Postgresql la presencia de cláusula HAVING convierte una consulta en una consulta agrupada, incluso sin una cláusula GROUP BY. Podría usar una subconsulta en su lugar:
stmt = db.session.query(
Location,
Location.distance(53.6209798282177,
13.96948162900808).label('distance')).\
subquery()
location_alias = db.aliased(Location, stmt)
locations = db.session.query(location_alias).\
filter(stmt.c.distance < 25).\
order_by(stmt.c.distance).\
all()