Django Сортировать по рассчитанному полю - PullRequest
2 голосов
/ 16 февраля 2010

Используя логику расстояния из этого поста , я получаю обратно правильно отфильтрованный набор объектов с этим кодом:

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 () по соображениям производительности (один запрос против одного запроса на каждое потенциальное местоположение в моей базе данных). Пара вопросов:

  1. Как я могу отсортировать мой список по расстоянию? Даже снимая родную сортировку, которую я определил в моей модели, и используя "order_by ()", она все равно сортируется по чему-то другому (я полагаю, id).
  2. Я ошибаюсь из-за производительности, и Django оптимизирует запрос, поэтому я должен использовать extra () вместо этого?
  3. Это абсолютно неправильный способ сделать это, и я должен использовать гео-библиотеку вместо того, чтобы раскатывать это вручную, как путц?

1 Ответ

1 голос
/ 16 февраля 2010

Чтобы ответить на ваши вопросы в обратном порядке:

Re 3) Да, вы обязательно должны использовать преимущества PostGIS и GeoDjango, если вы работаете с геопространственными данными. Просто глупо не делать этого.

Re 2) Я не думаю, что вы вполне могли бы заставить Django выполнить этот запрос за вас, используя .extra () (исключая принятие этого билета ), но это отличный кандидат для нового Метод .raw () в Django 1.2 (см. ниже).

Re 1) Вы получаете список идентификаторов из вашего первого запроса, а затем используете запрос «in» для получения QuerySet объектов, соответствующих этим идентификаторам. Ваш второй запрос не имеет доступа к рассчитанному расстоянию от первого запроса; это просто получение списка идентификаторов (и не важно, в каком порядке вы предоставляете эти идентификаторы).

Возможные решения (если не считать всего этого и использовать GeoDjango):

  1. Обновление до бета-версии Django 1.2 и использование new .raw () метода . Это позволяет Django интеллектуально интерпретировать результаты необработанного SQL-запроса и превращать его в QuerySet реальных объектов модели. Что уменьшит ваши текущие два запроса в один и сохранит порядок, указанный в SQL. Это лучший вариант, если вы можете выполнить обновление.

  2. Не беспокойтесь о создании набора запросов Django или объектов модели Django, просто добавьте все необходимые поля в необработанный SQL SELECT, а затем используйте эти строки прямо из курсора. Может не подойти, если вам понадобятся методы моделей и т. Д.

  3. Выполните третий шаг в коде Python, где вы выполняете итерацию по набору запросов и создаете список объектов модели Python в том же порядке, что и список идентификаторов, полученный из первого запроса. Вернуть этот список вместо QuerySet. Не сработает, если вам понадобится дальнейшая фильтрация по линии.

...