Django:子查询的注释

时间:2018-04-13 14:53:17

标签: python django subquery geodjango

我尝试使用Django 2.0.3和PostGIS(GeoDjango)函数使用最近的Station的{​​{1}}来注释id的查询集。

简化Station型号:

Station

我遇到的问题是尝试计算最近距离,这涉及注释引用外部查询集中class Station(models.Model): name = models.CharField(max_length=128) location = models.PointField() objects = StationQuerySet.as_manager() 的子查询。

location

from django.db.models import OuterRef, Subquery from django.contrib.gis.db.models.functions import Distance class StationQuerySet(models.QuerySet): def add_nearest_neighbour(self): ''' Annotates each station with the id and distance of the nearest neighbouring station ''' # Get Station model Station = self.model # Calculate distances to each station in subquery subquery_with_distance = Station.objects.annotate(distance=Distance('location', OuterRef('location')) / 1000) # Get nearest from subquery nearest = subquery_with_distance.order_by('distance').values('id')[0] return self.annotate( nearest_station_id=Subquery(nearest) ) 会导致以下错误:

distance = Station.objects.annotate(distance=Distance('location', OuterRef('location')) / 1000)

错误:

from apps.bikeshare.models import Station
stations = Station.objects.add_nearest_neighbour()

1 个答案:

答案 0 :(得分:0)

想出了一个使用原始查询来查找最近的工作站的工作,并从子查询中选择id和距离,下面是奖励说明:

class StationQuerySet(models.QuerySet):

    def nearest_neighbour(self):
        '''
        Creates a RawQuerySet of each station with the id and distance of the nearest neighbouring station
        '''
        # Have to execute the query in order to get the list of ids to inject
        ids = tuple(self.values('id').values_list('id', flat=True))

        return self.raw('''
               SELECT
                 A0.id   as id,
                 SUB.closest_id,
                 SUB.closest_distance
               FROM "bikeshare_station" A0
                 CROSS JOIN LATERAL (
                            SELECT
                              B0.id   as closest_id,
                              st_distance_sphere(A0.location, B0.location) as closest_distance
                            FROM "bikeshare_station" B0
                            WHERE A0.id != B0.id
                            ORDER BY A0.location <-> B0.location
                            limit 1
                            ) SUB
               WHERE A0.id IN %s;
           ''', [ids])

用法

您可以将查询集调用链接在一起以在查找最近邻居之前过滤查询集:

query = Station.objects.filter(name='Albert Gate, Hyde Park')
closest_stations = query.nearest_neighbour()
station = closest_stations[0]
station.name
[out]: 'Albert Gate, Hyde Park'
station.closest_distance
[out]: 133.52459069
station.closest_id
[out]: 6369

SQL说明

此类子查询称为相关子查询,因为它引用外部查询中的列。此外,我需要选择有关最近的电台的多条信息(iddistance等)。

子查询放在FROM子句中,允许选择多个列。需要LATERAL连接以允许子查询引用FROM列表中的兄弟表。通过子查询返回单行,可以应用CROSS连接以基于笛卡尔积而不是共享列来形成连接表。

子查询使用PostGIS <->运算符,它可以更有效地按工作站之间的距离对表格进行排序,并st_distance_sphere在点之间进行精确的距离计算。