HAVING
của bạn được xử lý chính xác, nhưng bạn đang chuyển nó biểu thức sai. Có vẻ như bạn đang sử dụng Python 2, vì so sánh quan hệ giữa một chuỗi và một số nguyên
'distance' < 25
không đưa ra một ngoại lệ, nhưng đánh giá thành False
thay thế. Nói cách khác, truy vấn của bạn bằng
locations = db.session.query(...).having(False).all()
điều này giải thích tại sao bạn không nhận được kết quả nào:tất cả các hàng được lọc ra rõ ràng bằng mệnh đề HAVING, như được thấy trong phiên bản in:
...
HAVING false = 1 -- remove all rows
Giải pháp là sử dụng một cấu trúc phù hợp, chẳng hạn như column()
, để tạo ra biểu thức:
locations = db.session.query(...).having(column('distance') < 25).all()
Bạn không nên bọc biểu thức mục danh sách chọn phức tạp trong select()
, đại diện cho một câu lệnh SELECT. Gắn nhãn cho text()
như là:
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')
hoặc xây dựng biểu thức bằng mô hình:
(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')
Bạn có thể cải thiện khả năng đọc cấu trúc truy vấn của mình bằng cách tạo một hàm cho khoảng cách vòng tròn lớn và với một chút công việc, bạn có thể triển khai phương pháp kết hợp
trên 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()
Lưu ý rằng cách bạn sử dụng HAVING để loại bỏ các hàng không thuộc nhóm không phải là cách di động. Ví dụ:trong Postgresql, sự hiện diện của mệnh đề HAVING chuyển một truy vấn thành một truy vấn được nhóm, ngay cả khi không có mệnh đề GROUP BY. Thay vào đó, bạn có thể sử dụng một truy vấn con:
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()