Django Сортировка по вычисляемому полю

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

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. Это совершенно неправильный способ сделать это, и я должен использовать географическую библиотеку вместо того, чтобы раскатывать это вручную, как путц?

person Tom    schedule 16.02.2010    source источник


Ответы (1)


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

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

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

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

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

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

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

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

person Carl Meyer    schedule 16.02.2010
comment
Спасибо, это все, о чем я подумал. Если я могу еще раз оскорбить вашу добродушие: я уже делал №2 раньше, я просто чувствовал себя грязным из-за этого. Есть ли способ просто создать объект набора запросов и вставить в него элементы в том порядке, в котором я хочу? Я собираюсь выбрать 2 или 3 на данный момент (сайт запускается сегодня вечером / завтра), но я хотел бы знать, возможно ли это. - person Tom; 17.02.2010
comment
Возможный? Конечно. Легко или просто или чисто? Нет, для этого нет причин. Если вам сейчас нужен грязный хакер, используйте №2 или №3. Позже правильный вариант - №1 или в любом случае GeoDjango. - person Carl Meyer; 17.02.2010