【问题标题】:Rails - has_one relationship with both WHERE & ORDER?Rails - has_one 与 WHERE 和 ORDER 的关系?
【发布时间】:2014-01-25 00:04:18
【问题描述】:

我在 User 模型中有以下关系。

has_many :subscriptions, dependent: :destroy
has_one :subscription, -> { where 'end_date >= ?', Date.today }

:subscription 本质上是他们最新的每月订阅。 :subscriptions 是他们所有以前的每月订阅(包括最新订阅)的数组。他们列出了 start_date、end_date、user_id、plan_id、payment_method。

当订阅计划升级或降级时,我将当前订阅的 end_date 更改为 Date.today(表示订阅今天结束),然后创建一个从今天开始到一个月结束的新订阅。

将 end_date 设置为 Date.today 的问题在于 WHERE 子句将获取已取消的订阅和新的订阅。但是会支持取消的订阅,因为 SQL 查询在 ORDER BY 类上生成了主 id 升序的大头钉。

ORDER BY "subscriptions"."id" ASC LIMIT 1

对于我的一生,我无法弄清楚如何将 ID 上的 ORDER BY 从 ASC 反转为 DESC。但我更喜欢根据 created_at 时间戳订购。以下是仅用于排序的适当代码,但除了 ORDER BY 子句之外,我还必须有 WHERE 子句。

has_one :subscription, -> { order 'created_at desc' }

有没有人想出如何将两者结合起来?

【问题讨论】:

  • 您是否尝试过用 ruby​​ 而非 SQL 对其进行排序?
  • 不,但如果 R​​ails 不允许在同一个 has_one 关系中组合 where 和 order 子句,这可能是需要的路由,这确实令人惊讶。

标签: sql ruby-on-rails ruby relationship has-one


【解决方案1】:

从 Rails 4 开始,当您调用 first 方法时,Rails 会自动添加按 id 排序。 (您可以调用take 来避免这种情况。)所以我猜has_one 在内部调用first

既然has_one 只是自动创建一个名为subscription 的方法,为什么不自己创建呢?:

def subscription
  subscriptions.where('end_date >= ?', Date.today).order("created_at DESC").take
end

如果您不喜欢这个,从您的问题中不清楚您是否尝试过这个:

has_one :subscription, -> { where('end_date >= ?', Date.today).order("created_at DESC") }

没有理由不能像这样链接这两个项目。但是我记得 Rails 4 first 将覆盖您的 order 指令并将其替换为 order(:id),因此这可能不起作用。你可以通过说@user.subscription.to_sql来确定。

【讨论】:

  • 将订单链接到 where 语句有效,不知道你能做到这一点。谢谢!
  • 我发现 Paul 是对的,如果使用 has_one,Rails 4 首先会覆盖您设置的顺序。但是我可以通过将子模型的默认范围设置为我想要的顺序来解决这个问题。
【解决方案2】:

问题似乎是您的设计在即将到期的订阅和将其定为“今天”之间不匹配,并声明只有一个订阅的结束日期可以 >= 今天。它们相互矛盾,因此您需要解决该问题。

其核心似乎是订阅开始日期包含在内,结束日期不包含在内 - 除非您将结束日期定义为“订阅有效的最后日期”而不是“最后一天后的第二天”订阅有效”,则您在同一天有两个订阅有效。

【讨论】:

  • 我误以为我希望它在 end_date 订购,我的意思是 created_at 时间戳。我提供了一个使用模型范围来实现我需要的答案。这在这种情况下有效,但可能不适用于其他情况,具体取决于模型的需求。
【解决方案3】:

除了创建一个自定义方法来处理这个问题,而不是使用 has_one 关系之外,我在排序查询时能找到的唯一解决方案是将订阅模型中的范围设置为按 created_at DESC 排序。

class Subscription < ActiveRecord::Base
  belongs_to :user
  belongs_to :plan

  default_scope order: 'created_at DESC'


  #Subscription Load (0.2ms)  SELECT "subscriptions".* FROM "subscriptions" WHERE "subscriptions"."user_id" = ? AND (end_date >= '2014-01-25') ORDER BY end_date DESC LIMIT 1  [["user_id", 2]]                                                                                                      

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 1970-01-01
    相关资源
    最近更新 更多