Django: фильтр по аннотированному значению

#mysql #django #python-3.x

#mysql #django #python-3.x

Вопрос:

У меня ситуация, когда у меня есть модель с именем trip . У каждого отключения есть departure_airport и an arrival_airport , которые являются связанными полями и оба являются частью airport модели. Каждый объект в модели airport имеет местоположение, представленное latitude и longitude полями.

Мне нужно иметь возможность принимать входные данные из двух (потенциально) отдельных местоположений аэропортов отправления и прибытия, используя что-то вроде формулы Haversine. Эта формула рассчитала бы расстояние от каждого аэропорта вылета / прибытия в базе данных до местоположения аэропортов, которые были приняты в качестве входных данных.

Трудная часть этого запроса заключается в том, что я аннотирую набор запросов на поездку местоположениями аэропортов отправления и прибытия, однако, поскольку существует два набора полей широты / долготы (по одному для каждого аэропорта) с одинаковым именем, и вы не можете использовать аннотированные поля в предложении sql where, я не могу использовать оба набора аэропортов в запросе.

Я полагаю, что решение состоит в использовании подзапроса к аннотированным полям, чтобы запрос выполнялся перед предложением where, однако я не смог определить, возможно ли это для этого запроса. Другой вариант — написать raw_sql.

Вот что у меня есть на данный момент:

 GCD_FORMULA_TO = """3961 * acos(
    cos(radians(%s)) * cos(radians(arrival_lat))
    * cos(radians(arrival_lon) - radians(%s))  
    sin(radians(%s)) * sin(radians(arrival_lat)))"""        

GCD_FORMULA_FROM = """3961 * acos(
    cos(radians(%s)) * cos(radians(departure_lat))
    * cos(radians(departure_lon) - radians(%s))  
    sin(radians(%s)) * sin(radians(departure_lat)))"""

location_to = Q(location_to__lt=self.arrival_airport_rad)
location_from = Q(location_from__lt=self.departure_airport_rad)

qs = self.queryset
    .annotate(arrival_lat=F('arrival_airport__latitude_deg'))
   .annotate(arrival_lon_to=F('arrival_airport__longitude_deg'))
    .annotate(departure_lat=F('departure_airport__latitude_deg'))
    .annotate(longitude_lon=F('departure_airport__longitude_deg'))
    .annotate(location_to=RawSQL(GCD_FORMULA_TO, (self.arrival_airport.latitude_deg, self.arrival_airport.longitude_deg, 
        self.arrival_airport.latitude_deg)))
    .annotate(location_from=RawSQL(self.GCD_FORMULA_FROM, (self.departure_airport.latitude_deg, self.departure_airport.longitude_deg, 
        self.departure_airport.latitude_deg)))
    .filter(location_to and location_from)

return qs
  

Есть идеи? Также открыты для других способов сделать это.

Ответ №1:

Вы делаете это сложным способом.

Если в вашем коде python есть пара местоположений, используйте это:

 from geopy.distance import distance

loc1 = (lat1, lng1)
loc2 = (lat2, lng2)
d = distance(loc1, loc2).km
  

Если вы запрашиваете базу данных, возможно, вы предпочли бы, чтобы она запускала PostGIS / Postgres, а не mysql,
таким образом, вы можете вычислить расстояние и принадлежность формы.
Синтаксис иногда неуклюж, но индексация работает отлично.
Вот пример вылета из лондонского аэропорта Хитроу:

 SELECT    a.airport_name,
          ST_Distance('SRID=4326; POINT(-0.461389 51.4775)'::geography,
                      ST_Point(a.longitude, a.latitude)) AS distance
FROM      arrival_airports a
ORDER BY  distance;
  

В качестве отдельного вопроса вы могли бы рассмотреть возможность определения ПРЕДСТАВЛЕНИЯ прибытия и / или отправления в вашей таблице, а затем ОБЪЕДИНЕНИЯ с отдельной моделью для каждого представления.

Комментарии:

1. В идеальном мире я бы разобрался с sql-запросом, но сейчас у меня нет времени. Это отлично работает. Спасибо!