Django:按距离排序对象

3
我有这个模型。
class Company(models.Model):
name = models.CharField(max_length = 50)
description = models.TextField()
latitude = models.FloatField()
longitude = models.FloatField()
owner = models.ForeignKey(User, on_delete = models.CASCADE, related_name = "company_owner")
category = models.ForeignKey(Category, on_delete = models.CASCADE)
def __str__(self):
    return self.name
class Meta:
    verbose_name_plural = "Companies"

def get_absolute_url(self):
    return reverse('category_list')
    #if want to redirect to its detail page then
    # return reverse('company_detail' ,kwargs = {'pk' : self.pk})

def get_distance(self):
    ip = get('https://api.ipify.org').text
    reader = geoip2.database.Reader('categories/GeoLite2-City.mmdb')
    response = reader.city(ip)
    current_lat = response.location.latitude
    current_lon = response.location.longitude
    comp_lat = self.latitude
    comp_lon = self.longitude
    R = 6373.0

    lat1 = radians(current_lat)
    lon1 = radians(current_lon)
    lat2 = radians(comp_lat)
    lon2 = radians(comp_lon)

    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
    c = 2 * atan2(sqrt(a), sqrt(1 - a))

    distance = R * c
    return(distance)

我已经从get_distance()函数中获取了用户位置和公司位置之间的距离。但是如何按升序排序这些距离呢?由于距离会因用户的不同位置而异,所以我不能将距离存储在数据库中。我想打印按距离升序排序的对象。


你考虑过使用GeoDjango吗?这样你就可以使用距离排序了。 - mfrackowiak
2个回答

0

由于这个问题仍未有被接受的答案,而且已经有几十位同行看到了它,我决定来尝试回答一下。

首先,我认为BernhardVallant和mrfackowiak已经指出了正确的解决方案。我只是想说明一下代码可能会是什么样子。

将Geodjango添加到您的项目中

从官方文档可以看出,“GeoDjango旨在成为世界级地理Web框架”。基本上,它使您能够以各种方式操作地理数据(坐标、光栅、投影)(计算距离、基于地理形状过滤对象等)。

要设置它需要几个步骤,已经有很多人进行了详细的说明。 您可以从官方文档的教程开始。

更新您的模型

首先,导入GeoDjango的模型和用于地理数据的特殊对象。然后,使用以下更改更新您的模型。

# models.py
from django.contrib.gis.db import models
from django.contrib.gis.geos import GEOSGeometry, fromstr

# Company model inherits from GeoDjango's model
class Company(models.Model): 

    ...  # your other fields go here
    latitude = models.FloatField()
    longitude = models.FloatField()
    geo_location = models.PointField(null=True) # New field

    # New method to generate geo_location from lat, lng
    def create_geo_location(self):
        self.geo_location = fromstr(f'POINT({self.lng} {self.lat})', srid=4326)

    # Overwrite save() to use create_geo_location()
    def save(self, **kwargs):
        """ Creates a geo_location value (Point), if no prior-value exist"""
        if not self.geo_location:
            self.create_geo_location()

在这里,您将不会使用get_distance()方法,而是将逻辑移动到视图中。

更新您的views.py文件

您的视图应该如下所示:

# views.py
from <your-app>.models import Company
from decimal import Decimal
from django.contrib.gis.geos import fromstr
from django.contrib.gis.db.models.functions import Distance 
from django.contrib.gis.measure import D 

class CompanyListView(ListView):
    context_object_name = 'companies'

    # Get user lat and lng using the logic in your get_distance() method and 
    # .. transom the values to Decimal 
    # ex. user_lat, user_lng = Decimal(45.5260525), Decimal(-73.5596788) 

    # user location is a geographic point value with a specific projection (srid)
    user_location = fromstr(f'POINT({user_lng} {user_lat})', srid=4326)

    queryset = Company.objects.filter(geo_location__dwithin=(user_location, D(m=2000)))  
       .annotate(distance_to_user = Distance("geo_location", user_location)) 
       .order_by("distance_to_user") 

这个查询将获得离用户2公里距离以内的所有公司实例。它还将使用annotate创建一个名为distance_to_user的新变量,在其中存储距离(以米为单位)。最后,它将对结果进行排序。

关于地理数据和查询,我没有解释一些细节,但如果您要使用GeoDjango,学习一些关于它们的知识会更好。希望这有所帮助。


0

最好的解决方案是研究使用GeoDjango,它使您能够进行空间查询

除了其他一些事情,您还可以进行距离查找,这主要是您要寻找的东西。然后,所有查询功能都驻留在数据库中,使用适当的数据库扩展(例如PostGIS用于postgres)。如果您无法使用GeoDjango,请尝试进行原始SQL查询,例如请参见此问题


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接