0
votes

Comment puis-je renvoyer des données supplémentaires lors de l'utilisation d'une requête brute sur un modèle utilisant Django?

Je suis nouveau sur django et j'ai une question concernant les requêtes db.

J'utilise actuellement une requête brute pour obtenir des endroits dans un rayon de x d'une lat lng donnée. Ma requête est

class BusinessSerializer(serializers.ModelSerializer):
    distance = serializers.SerializerMethodField()

    def get_distance(self, obj):
        try:
            return obj.distance.mi
        except BaseException:
            return None

    class Meta:
        fields = ('name', 'distance')
        model = models.Business

et je sérialise les données en utilisant les éléments suivants:

queryset = Business.objects.filter(point__distance_lte=(pnt, D(mi=10)))
                .annotate(distance=Distance("point", pnt))

Je récupère les données qui correspondent au Business model. Cependant, je souhaite également récupérer la distance calculée dans la requête. Comment pourrais-je faire cela pour que la distance pour chaque ligne soit également renvoyée?

Edit: Le modèle commercial est:

class Business(models.Model):
    name = models.CharField(max_length=50, verbose_name="Name")
    lat = models.FloatField(verbose_name="Latitude",
                                          null=True,
                                          blank=True)
    lng = models.FloatField(verbose_name="Longitude",
                                          null=True,
                                          blank=True)
    approved = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

Le modèle commercial n'a pas de distance et il est calculé à la volée dans la requête. L'idée est d'avoir plusieurs entreprises et les utilisateurs peuvent rechercher des entreprises dans un rayon de x miles de leur propre latitude / longitude. Je suis en mesure de renvoyer les entreprises qui correspondent aux critères de rayon, mais j'aimerais également afficher la distance réelle, ce que je ne peux pas faire pour le moment.

ps S'il y a une meilleure façon de faire cela, j'aimerais en entendre parler :)

RÉPONDRE

Enfin, j'ai compris comment faire cela et pendant que j'y étais, j'ai également réussi à trouver une meilleure façon de faire ce qui précède.

Tout d'abord, plutôt que d'utiliser la formule Haversine; J'ai utilisé l' API de base de données GeoDjango pour filtrer les éléments de la base de données en fonction de la distance. Cela signifiait que je n'avais plus besoin d'utiliser des requêtes brutes. CEPENDANT - Pour cela, j'ai changé de MySQL à PostgreSQL avec l'extension PostGIS car il a de meilleures fonctionnalités pour les requêtes basées sur l'emplacement à exécuter en SQL. J'ai pu le faire car j'étais très tôt dans mon projet, donc cela n'a pas eu un impact énorme.

Voici un exemple de l'API GeoDjango:

  queryset = Business.objects.raw(query)

  serialized_data = serializers.serialize('json', queryset)

J'ai ensuite ajouté la distance au sérialiseur sans ajouter de distance au modèle:

   query = """SELECT id, (3959*acos(cos(radians(%2f))
   *cos(radians(lat))*cos(radians(lng)-radians(%2f))
   +sin(radians(%2f))*sin(radians(lat))))
   AS distance FROM businesses_business WHERE approved > 0 HAVING
   distance < %2f ORDER BY %s LIMIT %d OFFSET %d;""" % (
        float(lat),
        float(lng),
        float(lat),
        details['radius'],
        details['orderBy'],
        details['limit'],
        details['offset']
    )

Maintenant, quand j'utilise BusinessSerializer il renvoie aussi la distance :)

Jours heureux.


0 commentaires

3 Réponses :


0
votes

Un peu confus: votre jeu de requêtes devrait déjà vous donner l' id et les valeurs de distance . D'après ce que je peux dire lorsque vous le sérialisez, vous devriez déjà avoir la valeur de la distance .

En supposant que vous ayez un champ de distance dans vos modèles, vous pouvez simplement parcourir l'ensemble de requêtes renvoyé pour voir les valeurs de distance, par exemple

class Person(models.Model):
        first_name = models.CharField(...)
        last_name = models.CharField(...)
        birth_date = models.DateField(...)
    
>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
       print(p)

Cela donnera toutes les valeurs de l'entité, vous pouvez donc appliquer la même logique dans votre cas.


2 commentaires

Hey, merci pour votre réponse. Le modèle lui-même n'a pas de champ de distance . La distance est calculée en utilisant la lat / long . Si j'exécute la requête dans mysql, elle renvoie l'identifiant et la distance comme vous l'avez mentionné. Cependant, je pense que le fait que j'utilise le .raw() sur Business modèle d' Business , cela finit par renvoyer l'entreprise complète selon le modèle d'entreprise.


@UsmanJ Je ne suis pas sûr de la meilleure méthode pour cela car je n'ai pas touché Django depuis un moment. Mais si le code propre n'est pas un problème, ajoutez simplement un champ de distance pour mettre à jour la valeur, puis renvoyez-le au modèle ( exemple ). Vous pouvez bien sûr, simplement exécuter la requête à l'aide d'un pilote - mappage directement à la base de données, puis extraire les valeurs de cette façon. La méthode de base de données peut également être efficace pour éviter de recalculer les valeurs si les valeurs sont déjà présentes dans les colonnes et que la valeur du rayon et l'emplacement de l'entreprise restent uniformes.



0
votes

La distance sera incluse dans le jeu de requêtes SQL brut, mais elle sera probablement supprimée par le sérialiseur.

avez-vous essayé de l'ajouter à l'attribut de champs dans le sérialiseur?

serialized_data = serializers.serialize('json', queryset, fields=('name','distance')) 


2 commentaires

Merci pour votre réponse. J'ai essayé cela, mais mon modèle ne sait pas ce qu'est la distance et je pense que c'est pourquoi même si je mets fields=('name', 'distance') dans le sérialiseur, il ne renvoie pas la distance. Il finit par ne renvoyer que le name .


Voyez-vous la distance si vous examinez le jeu de requêtes? Si tel est le cas, vous devrez peut-être aller plus loin dans la personnalisation du sérialiseur



0
votes

RÉPONDRE

Enfin, j'ai compris comment faire cela et pendant que j'y étais, j'ai également réussi à trouver une meilleure façon de faire ce qui précède.

Tout d'abord, plutôt que d'utiliser la formule Haversine; J'ai utilisé l' API de base de données GeoDjango pour filtrer les éléments de la base de données en fonction de la distance. Cela signifiait que je n'avais plus besoin d'utiliser des requêtes brutes. CEPENDANT - Pour cela, j'ai changé de MySQL à PostgreSQL avec l'extension PostGIS car il a de meilleures fonctionnalités pour les requêtes basées sur l'emplacement à exécuter en SQL. J'ai pu le faire car j'étais très tôt dans mon projet, donc cela n'a pas eu un impact énorme.

Voici un exemple de l'API GeoDjango:

class BusinessSerializer(serializers.ModelSerializer):
    distance = serializers.SerializerMethodField()

    def get_distance(self, obj):
        try:
            return obj.distance.mi
        except BaseException:
            return None

    class Meta:
        fields = ('name', 'distance')
        model = models.Business

J'ai ensuite ajouté la distance au sérialiseur sans ajouter de distance au modèle:

queryset = Business.objects.filter(point__distance_lte=(pnt, D(mi=10)))
                .annotate(distance=Distance("point", pnt))

Maintenant, quand j'utilise BusinessSerializer il renvoie aussi la distance :)

Jours heureux.


0 commentaires