Django如何为ForeignKey关联模型添加注解实现bookmark.spot.distance访问
Django的annotate()方法默认仅会为当前查询的主模型(即此场景下的Bookmark)添加计算字段,不会自动将字段透传挂载到外键关联的模型实例上——即使你使用select_related预加载了关联的Spot数据,这个规则也不会变。
要实现bookmark.spot.distance的访问效果,可根据业务场景选择以下两种方案:
方案1:遍历结果手动挂载属性(零额外查询开销,改动最小)
这是性价比最高的实现方式,仅需两步调整:
- 给Bookmark查询集加上
select_related("spot"),预加载关联的Spot数据,避免循环访问时产生N+1查询 - 遍历结果时,手动将Bookmark实例上的distance属性赋值给对应的Spot实例
参考代码:
ref_location = Point(query.longitude, query.latitude, srid=4326) bookmarks = Bookmark.objects.filter( user_id=user_id ).select_related("spot").annotate( distance=Distance("spot__coords", ref_location) ) for bookmark in bookmarks: bookmark.spot.distance = bookmark.distance print(bookmark.spot.distance) # 可正常获取距离值
该方案不需要修改原有查询逻辑,不会产生额外的数据库查询,适配绝大多数场景。
方案2:调整查询入口,直接在Spot模型上加注解
如果你后续的业务逻辑主要围绕Spot实例展开,不需要额外操作Bookmark的字段,可以直接反转查询逻辑,从Spot模型出发过滤关联的用户收藏记录,这样distance注解会直接挂载在Spot实例上,不需要额外赋值:
ref_location = Point(query.longitude, query.latitude, srid=4326) # 过滤当前用户收藏的Spot,直接给Spot加距离注解 bookmarked_spots = Spot.objects.filter( bookmarks__user_id=user_id ).annotate( distance=Distance("coords", ref_location) ).distinct() # 加distinct避免同个Spot被多次收藏时返回重复结果 for spot in bookmarked_spots: print(spot.distance) # 直接访问即可
提示:如果你的Bookmark模型已经配置了
unique_together = ("user", "spot")约束,保证同一个用户不会重复收藏同一个Spot,可以去掉查询中的.distinct()。
内容的提问来源于stack exchange,提问作者HoJin
相关产品推荐
相关产品推荐

