extra()表达式中的Django外键

extra()表达式中的Django外键,django,django-models,django-filter,django-select-related,Django,Django Models,Django Filter,Django Select Related,我试图使用Django extra()方法来过滤某个半径内的所有对象,就像下面的回答:但是我对“gcd”表达式有一些问题,因为我必须通过两个外键关系到达纬度和经度,而不是使用直接的模型字段 特别是,我有一个经验班: class Experience(models.Model): starting_place_geolocation = models.ForeignKey(GooglePlaceMixin, on_delete=models.CASCADE,

我试图使用Django extra()方法来过滤某个半径内的所有对象,就像下面的回答:但是我对“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'])

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

当您使用
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复制粘贴)

另外,您还可以面对
设置输出\u字段
错误。然后必须将整个距离表达式包装到
ExpressionWrapper
中,并为其提供一个
output\u field=models.DecimalField()
参数

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