【问题标题】:How can I return additional data when using a raw query on a model using Django?在使用 Django 的模型上使用原始查询时,如何返回其他数据?
【发布时间】:2021-02-22 15:26:05
【问题描述】:

我是 django 新手,有一个关于 db 查询的问题。

我目前正在使用原始查询来获取给定 lat lng x 半径内的位置。我的查询是

   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']
    )

我正在使用以下方法对数据进行序列化:

  queryset = Business.objects.raw(query)

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

我取回与业务模型匹配的数据。但是,我也想收到查询中计算的距离。我怎么能这样做,以便也返回每行的距离?

编辑:商业模式是:

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)

业务模型中没有distance,它是在查询中动态计算的。这个想法是拥有多个企业,用户可以在他们自己的 lat/lng x 英里范围内搜索企业。我可以退回符合半径标准的商家,但我也想显示实际距离,这是我目前无法做到的。

附言如果有更好的方法来做到这一点,那么我很想听听 :)

回答

最后我想出了如何做到这一点,在我做这件事的同时,我还设法找到了一种更好的方法来完成上述工作。

首先,而不是使用Haversine公式;我使用GeoDjango Database API 根据距离过滤数据库中的项目。这意味着我不再需要使用原始查询。 然而 - 为此,我使用 PostGIS 扩展器从 MySQL 更改为 PostgreSQL,因为它具有更好的功能,可以在 SQL 中运行基于位置的查询。我能够做到这一点,因为我在我的项目中很早,所以它没有产生巨大的影响。

以下是 GeoDjango API 的示例:

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

然后我将distance 添加到序列化程序中,而不将distance 添加到模型中:

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

现在,当我使用 BusinessSerializer 时,它也会返回 distance :)

快乐的日子。

【问题讨论】:

    标签: python mysql django


    【解决方案1】:

    有点困惑:您的查询集应该已经为您提供了 iddistance 值。据我所知,当你序列化它时,你应该已经有了distance 的值。

    假设您的模型中有一个 distance 字段,您可以遍历返回的查询集以查看距离值,例如

    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)
    

    这将给出实体中的所有值,因此您可以在您的案例中应用相同的逻辑。

    【讨论】:

    • 您好,谢谢您的回复。模型本身没有distance 字段。 distance 正在使用 lat/long 计算。如果我在 mysql 中运行查询,它会返回您提到的 Id 和距离。但是,我认为我在Business 模型上使用.raw() 的事实最终会根据业务模型返回完整的业务。
    • @UsmanJ 我不确定最好的方法,因为我有一段时间没有接触过 Django。但是,如果不担心干净的代码,那么只需添加一个 distance 字段来更新值,然后将其返回到模板 (example)。当然,您可以只使用驱动程序运行查询 - 直接映射到数据库,然后以这种方式提取值。如果值已经存在于列中并且半径值和业务位置保持一致,则数据库方法也可以有效地避免重新计算值。
    【解决方案2】:

    distance 将包含在原始 sql 查询集中,但它可能会被序列化程序删除。

    您是否尝试将其添加到序列化程序中的字段属性中?

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

    【讨论】:

    • 感谢您的回复。我试过这个,但是我的模型不知道distance 是什么,我认为这就是为什么即使我将fields=('name', 'distance') 放入序列化程序中,它也不会返回距离。它最终只返回name
    • 如果您检查查询集,您是否看到distance?如果是这样,您可能需要进一步自定义序列化程序
    【解决方案3】:

    回答

    最后我想出了如何做到这一点,在我做这件事的同时,我还设法找到了一种更好的方法来完成上述工作。

    首先,而不是使用Haversine公式;我使用GeoDjango Database API 根据距离过滤数据库中的项目。这意味着我不再需要使用原始查询。 然而 - 为此,我使用 PostGIS 扩展器从 MySQL 更改为 PostgreSQL,因为它具有更好的功能,可以在 SQL 中运行基于位置的查询。我能够做到这一点,因为我在我的项目中很早,所以它没有产生巨大的影响。

    以下是 GeoDjango API 的示例:

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

    然后我将distance 添加到序列化程序中,而不将distance 添加到模型中:

    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
    

    现在当我使用BusinessSerializer 时,它也会返回distance :)

    快乐的日子。

    【讨论】:

      猜你喜欢
      • 2020-07-14
      • 1970-01-01
      • 1970-01-01
      • 2019-08-18
      • 2012-12-06
      • 2014-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多