Django Qeryset通过不直接相关的模型字段进行排序

时间:2019-10-18 18:23:16

标签: python django

我有以下模型设计:

class Boat(models.Model):    
    name = models.CharField(max_length=30, null=False)
    harbour = models.ForeignKey(Harbour, null=True, on_delete=models.SET_NULL)

class Harbour(models.Model):
    name = models.CharField(max_length=60)
    city = models.ForeignKey(City, null=True, related_name='city_harbours',  on_delete=models.SET_NULL)

class City(models.Model):
    name = models.CharField(max_length=80)
    county = models.CharField(max_length=30, null=True)


class NearestCity(models.Model):
    city = models.ForeignKey(City, related_name='city', null=False, on_delete=models.CASCADE)
    near_city = models.ForeignKey(City, related_name='near_city', null=False, on_delete=models.CASCADE)
    weight = models.DecimalField(null=False, max_digits=25, decimal_places=20)

简要说明:

  • Boat属于Harbour,而Harbour属于City

  • 并非所有城市都有与之相关的港口。

  • NearestCity是一个表,存储与其他城市的城市之间的距离:weight是一个十进制值,指示 city 来自_near_city_。 “权重”值越小,城市与near_city的距离就越近。例如:

city     near_city      weight
----    ---------       ------
London   Rome           2.210103
London   Manchester     0.113134

这意味着曼彻斯特比罗马离伦敦更近。

要解决的问题:

给出一个没有任何港口相关城市的名称,例如柏林,想要归还那些至少有一个港口相关城市的最近城市的所有船只。此Boat的查询集必须由weight DESC排序。

我真的是django queryset的新手,我尝试将annotatesubqueriesaggregations等一起使用来解决。但是我无法实现。

1 个答案:

答案 0 :(得分:1)

我会尝试raw sql。这样的事情应该起作用:

city = selected_city_pk
sql = """
    SELECT b.name, nc.weight, nc.near_city_id
    FROM appname_nearestcity nc
    JOIN appname_city c ON near_city_id = c.id
    JOIN appname_harbour h ON near_city_id = h.city_id
    JOIN appname_boat b ON h.id = b.harbour_id
    WHERE nc.city_id = %s
    ORDER BY nc.weight, b.name
"""
results = Boat.objects.raw(sql, [city,])

for r in results: print(r.id, r.name, r.weight, r.near_city_id)
# boat_id, boat_name, weight, near_city_id
>> 14 b-0-h-sochi-0 10 6
>> 15 b-0-h-sochi-1 10 6
>> 16 b-0-h-sochi-2 10 6
>> 17 b-1-h-sochi-2 10 6
>> 18 b-2-h-sochi-2 10 6
>> 11 b-0-h-rome-0 55 5
>> 12 b-1-h-rome-0 55 5
>> 13 b-2-h-rome-0 55 5
>> 4 b-0-h-brasilia-0 56 4
>> 7 b-0-h-brasilia-1 56 4
>> 10 b-0-h-brasilia-2 56 4
>> 5 b-1-h-brasilia-0 56 4
>> 8 b-1-h-brasilia-1 56 4
>> 6 b-2-h-brasilia-0 56 4
>> 9 b-2-h-brasilia-1 56 4
>> 1 b-0-h-beijin-0 93 2
>> 3 b-0-h-beijin-1 93 2
>> 2 b-1-h-beijin-0 93 2

请记住用数据库中的实际表名替换表名,因为Django向其添加了应用程序名称前缀。