0

我正在尝试使用 Django extra() 方法过滤一定半径内的所有对象,就像在这个答案中一样:http ://stackoverflow.com/questions/19703975/django-sort-by-distance/26219292但是我在使用“gcd”表达式时遇到了一些问题,因为我必须通过两个外键关系而不是使用直接模型字段来达到纬度和经度。

特别是,我有一个体验课:

class Experience(models.Model):   
      starting_place_geolocation = models.ForeignKey(GooglePlaceMixin, on_delete=models.CASCADE,
                                               related_name='experience_starting')
      visiting_place_geolocation = models.ForeignKey(GooglePlaceMixin, on_delete=models.CASCADE,
                                               related_name='experience_visiting')

具有同一个 GooglePlaceMixin 类的两个外键:

class GooglePlaceMixin(models.Model):
      latitude = models.DecimalField(max_digits=20, decimal_places=15)
      longitude = models.DecimalField(max_digits=20, decimal_places=15)
      ...

这是我通过起始位置过滤体验对象的代码:

    def search_by_proximity(self, experiences, latitude, longitude, proximity):

          gcd = """
                6371 * acos(
                cos(radians(%s)) * cos(radians(starting_place_geolocation__latitude))
                * cos(radians(starting_place_geolocation__longitude) - radians(%s)) +
                sin(radians(%s)) * sin(radians(starting_place_geolocation__latitude))
                          )
                          """
          gcd_lt = "{} < %s".format(gcd)

          return experiences \
              .extra(select={'distance': gcd},
               select_params=[latitude, longitude, latitude],
               where=[gcd_lt],
               params=[latitude, longitude, latitude, proximity],
               order_by=['distance'])

但是当我尝试调用外键对象“starting_place_geolocation__latitude”时,它返回此错误:

column "starting_place_geolocation__latitude" does not exist

我应该怎么做才能达到外键值?先感谢您

4

1 回答 1

1

当您使用时extra(应该避免,如文档中所述),您实际上是在编写原始 SQL。您可能知道,要从 ForeignKey 获取值,您必须执行 JOIN。使用 Django ORM 时,它将花哨的双下划线转换为正确的 JOIN 子句。但是SQL不能。而且您也不能手动添加 JOIN。这里正确的做法是坚持使用ORM,为sin、cos、弧度等定义一些自定义的数据库函数。这很容易。

class Sin(Func):
    function = 'SIN' 

然后像这样使用它:

qs = experiences.annotate(distance=Cos(Radians(F('starting_place_geolocation__latitude') )) * ( some other expressions)) 

请注意,花哨的双下划线再次出现并按预期工作您已经明白了。

如果您喜欢从 SO 复制粘贴,这是我的完整集合) https://gist.github.com/tatarinov1997/3af95331ef94c6d93227ce49af2211eb

PS你也可以面对set output_field错误。然后你必须将你的整个距离表达式包装起来ExpressionWrapper并提供一个output_field=models.DecimalField()参数。

于 2018-05-14T20:27:14.833 回答