Django:按距离对对象进行排序

时间:2019-01-29 12:38:52

标签: django

我有这个模特

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()函数获得了用户位置和公司位置之间的距离。但是,如何将距离按升序排序? 由于距离与用户的不同位置不同,因此无法将距离存储在数据库中。 我想打印按距离升序排列的对象

2 个答案:

答案 0 :(得分:0)

最好的解决方案是研究使用GeoDjango,它使您能够spatial queries

除了可以做的其他事情distance lookups,这主要是您正在寻找的东西。然后,所有查询功能都使用适当的数据库扩展名(例如PostGIS用于postgres)驻留在数据库内部。如果您不能使用GeoDjango,请尝试执行原始SQL查询,例如,参见this question

答案 1 :(得分:0)

由于这个问题仍然没有被接受的答案,并且有数十位研究员看到了它,所以我决定稍作讨论。

首先,我认为BernhardVallant和mrfackowiak已经指出了正确的解决方案。我将说明它的代码是什么样的。

将Geodjango添加到您的项目

来自官方文档“ GeoDjango打算成为世界级的地理Web框架”。基本上,它使您能够以各种方式(计算距离,基于地理形状过滤对象等)来操纵地理数据(坐标,栅格,项目)。

设置它需要一些步骤,许多人已经对此进行了详尽的解释。您可以从official documentation's tutorial开始。

更新您的模型

首先,导入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()方法,而是将逻辑移至视图。

更新您的视图。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公里之内的所有Company实例。它还将创建一个带注释的新变量,称为distance_to_user,它将存储距离(以米为单位)。最后,它将对结果进行排序。

有一些我没有解释的有关地理数据和查询的细节,但是如果您要使用GeoDjango,最好对它们有所了解。我希望这会有所帮助。