我试图使用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
我应该怎么做才能达到外键值?提前谢谢
答案 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()
参数。