根据相关的M2M字段对查询集进行排序

时间:2019-05-07 09:32:46

标签: python django

我目前正在尝试基于三种模型返回查询集。

位置模型

class Location(models.Model):
    ...
    lat = models.FloatField()
    lng = models.FloatField()

用户模型(从Django用户扩展)

class UserProfile(models.Model):
    ...
    user = OneToOneField(User)
    locations = ManyToManyField(Location)

案例模型

class Case(models.Model):
    ...
    owner = models.ForeignKey(User)
    completed = models.BooleanField()

我正在使用geopy返回接近输入经纬度的位置

def get_locations_nearby_coords(latitude, longitude, max_distance=None):
    """
    Return objects sorted by distance to specified coordinates
    which distance is less than max_distance given in kilometers
    """
    # Great circle distance formula
    gcd_formula = "6371 * acos(cos(radians(%s)) * cos(radians(lat)) * cos(radians(long) - radians(%s)) + sin(radians(%s)) * sin(radians(lat)))"
    distance_raw_sql = RawSQL(
        gcd_formula,
        (latitude, longitude, latitude)
    )
    qs = Location.objects.all().annotate(
        distance=distance_raw_sql).order_by('distance')
    if max_distance is not None:
        qs = qs.filter(distance__lt=max_distance)
    return qs

我要返回的是按每个locations完成的案例数排序的最接近的UserProfile十位

一个用户可以有多个位置,并且一个位置可以与许多用户相关联,因此我为什么要使用m2m字段。

我没有任何重复项,但是,如果某个位置有多个用户,则该位置可以在列表中显示两次,但是最终列表应切成10。

编辑:

试图弄清楚应该返回什么。

假设我有3个位置靠近我(这是已知数据)

1. Location 1 (Closest)
2. Location 2 
3. Location 3 (Furthest)

Location 1有1个用户,已完成3个案例。 Location 2有两个用户,用户1已完成10个案例,用户2已完成2个。Location 3拥有1个用户,具有4个完整案例。应该返回的内容如下

1. Location 2 - User 1 - 10 Cases
2. Location 3 - User 1 - 4 Cases
3. Location 1 - User 1 - 3 Cases
4. Location 2 - User 2 - 2 Cases

1 个答案:

答案 0 :(得分:1)

首先,我建议应用显式关系名称以在查询中使用它们。

要将完整的病例数附加到查询集,您可以使用以下方法:

qs = qs.annotate(completed_cases=Count('user__case', filter=Q(user__case__completed=True))

最后的切片与[:10]

一样小
qs = qs.order_by('distance', '-completed_cases`, 'id', )[:10]

id-为避免相同距离和案例数的随机性。

UPD

# finding locations
...
qs = qs.order_by('distance', 'id', )[:10]

# finding users with completed cases
final_qs = User.objects.filter(locations__in=qs, ).\
    annotate(
        completed_cases=Count('case', filter=Q(case__completed=True),
        location_id=F('locations__id'),
    ).order_by('-completed_cases', 'id')[:10]