在RoR中过滤嵌套的模型属性

时间:2018-06-14 19:58:29

标签: ruby-on-rails ruby

我有两个模型:Users和PaymentMethods,这个模型之间的关联是:

class User < ApplicationRecord
  has_many :payment_methods, dependent: :destroy
end

class PaymentMethod < ApplicationRecord
  belongs_to :user, optional: true 
end

我想在每个用户中循环并查看PaymentMethod的一个名为'period_end_date'的属性。所以我这样做:

@users = User.all
@users.each do |u|
  u.payment_methods.last.period_end_date
end

我收到此错误=&gt; NoMethodError:User :: ActiveRecord_Relation的未定义方法`payment_methods'

显示错误是因为我有2个测试用户,在第一个用户中,属性'period_end_date'中仍然没有数据且关联存在,但是为空,在第二个用户中属性中有数据,如果我比如,u.payment_methods.last.period_end_date我得=>&gt; 2018年6月13日星期三(仅限第二位用户)

我想在我的循环中仅过滤在PaymentMethod属性中具有数据的用户来摆脱=&gt; NoMethodError:User :: ActiveRecord_Relation的未定义方法`payment_methods'

我是怎么做到的?

感谢

1 个答案:

答案 0 :(得分:1)

  

我想在我的循环中仅过滤在PaymentMethod属性中具有数据的用户来摆脱=&gt; NoMethodError:未定义的方法`payment_methods&#39; for User :: ActiveRecord_Relation

实际问题似乎是您没有付款方式的用户(请参阅我对您的问题的评论)。

您有一些选择,具体取决于 您将如何使用结果。

1)当您从数据库中查询用户时,您可以在没有付款方式的情况下过滤掉用户:

@users = User.joins :payment_methods

2)如果@users必须包含没有付款方式的用户,则可以在循环播放时跳过它们:

@users.map do |user|
  next unless user.payment_methods.any?
  user.payment_methods.last.period_end_date
end

3)在致电payment_methods之前,您可以通过检查.last来保护。

User.all.map do |user|
  user.payment_methods.last.period_end_date if user.payment_methods.any?
end

4)您可以向用户添加period_end_date方法

class User < ApplicationRecord
  def period_end_date
    payment_methods.limit(1).pluck :period_end_date
  end
end

5)通过使用辅助方法

扩展#4,将#4推入关联
class User < ApplicationRecord
  has_many :payment_methods, class_name: 'PaymentMethod' do
    def last_period_end_date
      last.period_end_date if any?
    end
  end
end

你可以像这样打电话

User.all.map do |user|
  user.payment_methods.last_period_end_date
end

如果您真的关注PaymentMethod而没有period_end_date,请尝试以下方法:

6)当您从数据库中查询用户时,您仍然可以过滤用户

@users = User.joins(:payment_methods).where.not(payment_methods: { period_end_date: nil })

7)通过将where.not条件推送到PaymentMethod类的范围,可以简化这一点:

class PaymentMethod < ApplicationRecord
  scope :period_ends, -> { where.not period_end_date: nil }
end

并合并

@users = User.joins(:payment_methods).merge PaymentMethod.period_ends

注释

  • payment_methods.last没有指定订单,您应该设置一个(作为此链的一部分,当您指定关联或使用默认范围时),否则订单最多你的数据库可能不确定。

  • .includes(:payment_methods)急切加载付款方式并避免n + 1次查询

  • 听起来像是period_end_date可能是无效数据。考虑添加验证/数据库约束以防止这种情况发生