Django外键在extra()表达式中

时间:2018-05-13 16:24:00

标签: django django-models django-filter django-select-related

我试图使用Django extra()方法来过滤特定半径内的所有对象,就像在这个答案中一样:http://stackoverflow.com/questions/19703975/django-sort-by-distance/26219292但是我对&有一些问题#39; 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'])

但是当我尝试调用外键对象&#34; strarting_place_geolocation__latitude&#34;它返回此错误:

column "starting_place_geolocation__latitude" does not exist

我应该怎么做才能达到外键值?提前谢谢

1 个答案:

答案 0 :(得分:1)

当您使用extra时(应该避免,如文档中所述),您实际上是在编写原始SQL。您可能知道,要从ForeignKey获取值,您必须执行JOIN。使用Django ORM时,它会翻译那些花哨的双下划线来纠正JOIN子句。但是SQL不可能。而且你也无法手动添加JOIN。这里正确的方法是坚持使用ORM并为sin,cos,radians等定义一些自定义数据库函数。这很简单。

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

P上。 S.您还可能面临set output_field错误。然后你必须将整个距离表达式包装到ExpressionWrapper并提供output_field=models.DecimalField()参数。

相关问题