简体   繁体   中英

Django Queryset order by a not directly related model's field

I have the following model design:

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)

Brief explanation:

  • A Boat belongs to a Harbour and a Harbour belongs to a City .

  • Not all Cities have a harbour related.

  • NearestCity is a table where is stored how close is a city the rest of the cities: The weight is a decimal value that indicates how far is city from _near_city_. The more smaller is the 'weight' value, the more close is city to near_city. For example:

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

It means that Manchester is closer to London than Rome.

Problem to solve:

Given a name of a city without any harbour related, for instance Berlin, a want to return all the boats of those closest cities that do have at least one harbour related. This Boat's queryset must be ordered by weight DESC.

I am really newbie with django queryset and I tried to solved using annotate with subqueries , aggregations , etc.. but I could not achieve it.

I would try raw sql . Something like this should work:

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

Remember to replace table names with actual table names from your DB because Django adds app name prefixes to them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM