Question

Utilisation de la logique de distance de ce SO afficher , Je reviens un ensemble d'objets avec ce code correctement filtré:

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)

Le problème est que je ne peux pas comprendre comment garder la liste / queryset triés par la valeur de distance. Je ne veux pas faire cela comme un appel de méthode supplémentaire () pour des raisons de performance (une requête par rapport à une requête sur chaque emplacement potentiel dans ma base de données). Quelques questions:

  1. Comment puis-je trier ma liste par la distance? Même décollant le genre natif j'ai défini dans mon modèle et en utilisant « order_by () », il est toujours le tri par autre chose (id, je crois).
  2. Suis-je trompé sur la chose la performance et Django d'optimiser la requête, donc je devrais utiliser à la place supplémentaire ()?
  3. Est-ce la façon tout à fait tort de le faire et que je devrais utiliser la bibliothèque géo au lieu de la main-rouler cela comme un Putz?
Était-ce utile?

La solution

Pour répondre à vos questions dans l'ordre inverse:

Re 3) Oui, vous devriez certainement profiter de PostGIS et GeoDjango si vous travaillez avec des données géospatiales. Il est tout simplement stupide de ne pas.

Re 2) Je ne pense pas que vous pourriez tout à fait obtenir Django à faire cette requête pour vous en utilisant .extra () (sauf acceptation de ce billet ), mais il est un excellent candidat pour la nouvelle méthode .raw () dans Django 1.2 (voir ci-dessous).

Re 1) Vous obtenez une liste d'identifiants de votre première requête, puis en utilisant un « dans » requête pour obtenir un QuerySet des objets correspondant à ces ids. Votre deuxième requête n'a pas accès à la distance calculée de la première requête; il est juste aller chercher une liste des ids (et il ne se soucie pas pour vous fournir ces ids dans, que ce soit).

Les solutions possibles (à court d'amerrissage tout cela et en utilisant GeoDjango):

  1. Mise à niveau vers Django 1.2 beta et utiliser le nouvelle méthode .raw () . Cela permet à Django d'interpréter intelligemment les résultats d'une requête SQL brute et la transformer en un QuerySet d'objets modèles réels. Ce qui permettrait de réduire vos deux requêtes actuelles en un seul, et préserver l'ordre que vous spécifiez dans SQL. Ceci est la meilleure option si vous êtes en mesure de faire la mise à niveau.

  2. Ne pas la peine de construire un modèle Django Django queryset ou objets du tout, il suffit d'ajouter tous les champs dont vous avez besoin dans SQL SELECT premières et ensuite utiliser ces lignes directement à partir du curseur. Ne peut pas être une option si vous avez besoin des méthodes de modèle, etc plus tard.

  3. Effectuer une troisième étape dans le code Python, où vous itérer à travers queryset et construire une liste Python d'objets de modèle dans le même ordre que les ids liste que vous avez obtenu de retour de la première requête. Retour cette liste au lieu d'un QuerySet. Ne fonctionne pas si vous devez faire plus filtrer la ligne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top