Votre HAVING
est géré correctement, mais vous lui passez la mauvaise expression. Il semble que vous utilisiez Python 2, car la comparaison relationnelle entre une chaîne et un entier
'distance' < 25
ne lève pas d'exception, mais est évalué à False
Au lieu. En d'autres termes, votre requête est égale à
locations = db.session.query(...).having(False).all()
ce qui explique pourquoi vous n'obtenez aucun résultat :toutes les lignes sont explicitement filtrées par la clause HAVING, comme on le voit dans la version imprimée :
...
HAVING false = 1 -- remove all rows
Une solution consiste à utiliser une construction appropriée, telle que column()
, pour produire l'expression :
locations = db.session.query(...).having(column('distance') < 25).all()
Vous ne devez pas envelopper l'expression d'élément de liste de sélection complexe dans un select()
, qui représente une instruction SELECT. Soit étiqueter le text()
tel quel :
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')
ou construisez l'expression en utilisant le modèle :
(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')
Vous pouvez améliorer la lisibilité de la construction de votre requête en créant une fonction pour la distance du grand cercle , et avec un peu de travail, vous pourriez implémenter un méthode hybride
sur 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()
Notez que la façon dont vous utilisez HAVING pour éliminer les lignes non groupées n'est pas portable. Par exemple dans Postgresql la présence de clause HAVING transforme une requête en une requête groupée, même sans clause GROUP BY. Vous pouvez utiliser une sous-requête à la place :
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()