Сортировка Django по вычисляемому полю
Вопрос
Используя логику расстояния из этот ТАК-пост, я получаю правильно отфильтрованный набор объектов с помощью этого кода:
class LocationManager(models.Manager):
def nearby_locations(self, latitude, longitude, radius, max_results=100, use_miles=True):
if use_miles:
distance_unit = 3959
else:
distance_unit = 6371
from django.db import connection, transaction
cursor = connection.cursor()
sql = """SELECT id, (%f * acos( cos( radians(%f) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(%f) ) + sin( radians(%f) ) * sin( radians( latitude ) ) ) )
AS distance FROM locations_location HAVING distance < %d
ORDER BY distance LIMIT 0 , %d;""" % (distance_unit, latitude, longitude, latitude, int(radius), max_results)
cursor.execute(sql)
ids = [row[0] for row in cursor.fetchall()]
return self.filter(id__in=ids)
Проблема в том, что я не могу понять, как сохранить сортировку списка/набора запросов по значению расстояния.Я не хочу делать это как вызов метода extra() по соображениям производительности (один запрос вместо одного запроса для каждого потенциального местоположения в моей базе данных).Пара вопросов:
- Как я могу отсортировать свой список по расстоянию?Даже если убрать встроенную сортировку, которую я определил в своей модели, и использовать «order_by()», она все равно будет сортироваться по чему-то другому (полагаю, по идентификатору).
- Я ошибаюсь насчет производительности, и Django оптимизирует запрос, поэтому вместо этого мне следует использовать extra()?
- Это совершенно неправильный способ сделать это, и мне следует использовать географическую библиотеку вместо того, чтобы прокручивать это вручную, как дебилизм?
Решение
Чтобы ответить на вопросы в обратном порядке:
Касательно 3) Да, вам обязательно следует воспользоваться преимуществами PostGIS и GeoDjango, если вы работаете с геопространственными данными.Просто глупо этого не делать.
Re 2) Я не думаю, что вы могли бы заставить Django выполнить этот запрос за вас, используя .extra() (за исключением принятия этот билет), но он является отличным кандидатом на роль нового метода .raw() в Django 1.2 (см. ниже).
Re 1) Вы получаете список идентификаторов из вашего первого запроса, а затем используете запрос «in», чтобы получить QuerySet объектов, соответствующих этим идентификаторам.Ваш второй запрос не имеет доступа к вычисленному расстоянию из первого запроса;он просто извлекает список идентификаторов (и не имеет значения, в каком порядке вы предоставляете эти идентификаторы).
Возможные решения (если не отказываться от всего этого и использовать GeoDjango):
Обновитесь до бета-версии Django 1.2 и используйте новый метод .raw().Это позволяет Django разумно интерпретировать результаты необработанного SQL-запроса и превращать его в QuerySet реальных объектов модели.Это сократит ваши текущие два запроса в один и сохранит порядок, указанный вами в SQL.Это лучший вариант, если вы можете сделать обновление.
Не беспокойтесь о создании набора запросов Django или объектов модели Django, просто добавьте все необходимые поля в необработанный SQL SELECT, а затем используйте эти строки непосредственно из курсора.Может быть не вариант, если вам позже понадобятся методы модели и т.д.
Выполните третий шаг в коде Python, где вы перебираете набор запросов и создаете список объектов модели Python в том же порядке, что и список идентификаторов, который вы получили в результате первого запроса.Верните этот список вместо QuerySet.Не сработает, если вам нужно выполнить дальнейшую фильтрацию по строке.