Rails中如何将Subscription的current_version实例方法改写为关联?
问题解答
1. 现有排序查询是否会重复返回订阅
你写的查询不会出现同个订阅重复返回的问题:
- 因为你已经按
subscriptions.id(订阅表主键)分组,数据库会保证每个分组仅返回一条记录,所以每个订阅只会出现一次 - 两点可优化的注意项:
- 目前用的
joins是内连接,会自动过滤掉没有关联versions记录的订阅,如果需要保留这部分数据,建议替换为left_joins - 如果你的查询没有关联
users表,group语句里的users.id是多余的,可能会触发数据库报错,可直接简化为group("subscriptions.id")
- 目前用的
- 排序逻辑是正确的,
MAX(versions.authorized_at)取到的就是每个订阅最新的授权日期
2. 正确实现current_version关联
报错原因
你原来的写法错误是因为has_one关联的lambda作用域是在Version模型的上下文下执行,不是在Subscription实例上下文,所以无法调用versions方法;同时last是立即触发查询的执行方法,不能写在关联的作用域定义中。
正确写法
has_one关联会自动匹配外键,且默认返回作用域匹配到的第一条记录,所以我们只需要在作用域里完成筛选、排序即可,逻辑和你原来的实例方法完全一致:
# subscription.rb has_one :current_version, -> { authorized.order(authorized_at: :desc) }, class_name: "Version", inverse_of: :subscription
注意:原来的inverse_of值写错了,应该是小写的subscription对应Version模型里的belongs_to :subscription关联名。
额外优化
使用这个关联后,你可以直接用预加载避免N+1查询,排序逻辑也可以简化为:
Subscription .left_joins(:current_version) .group("subscriptions.id") .order("versions.authorized_at ASC NULLS LAST")
NULLS LAST适配PostgreSQL,可以把没有授权版本的订阅排在列表最后,其他数据库可以替换为对应的空值排序语法。
内容的提问来源于stack exchange,提问作者pinkfloyd90
相关产品推荐
相关产品推荐

