【问题标题】:Rails NoMethodError (undefined method `<<' for #<ActiveRecord::Relation []>):Rails NoMethodError(#<ActiveRecord::Relation []> 的未定义方法 `<<'):
【发布时间】:2021-09-24 23:27:22
【问题描述】:

幕后故事

我目前正在将我的 Ruby on Rails 应用程序更改为多数据库配置。切换的主要原因是将我的 Member(User) 和 Profile 表放在单独的数据库中,可以从另一个 RoR 应用程序访问;因此,除了使用 OAuth 和 Doorkeeper 之外,我还可以拥有单点登录功能。这是一个许多小时的项目,有许多疯狂的障碍。最后今晚似乎一切正常,直到我进行了规范测试,而我今天所做的工作之一就是扔旗子。

这里是问题的适当代码,为简洁起见进行了简化。

任何帮助将不胜感激。谢谢。

模型

app/models/members_record.rb

class MembersRecord < ApplicationRecord
    self.abstract_class = true
  
    connects_to database: { writing: :members, reading: :members }
end

app/models/member.rb

class Member < MembersRecord
    devise  :database_authenticatable, :registerable,
            :recoverable, :rememberable, :trackable, 
            :validatable, :confirmable
  
    has_one :member_map,    dependent: :destroy
    has_one :profile,       dependent: :destroy
    has_one :site_profile,  dependent: :destroy
    
    has_many :approved_group_members, -> { approved }, class_name: "GroupMember"
    has_many :groups, through: :approved_group_members, class_name: "Group", source: :group

app/models/group.rb

class Group < ApplicationRecord
    belongs_to :owner, class_name: 'Member'
    belongs_to :organization, optional: true
  
    has_many :group_members, dependent: :destroy
  
    # Many of the lines below no longer work with Multiple DBs, as we can not INNER JOIN across DBs.
    # They have been commented out and methods built in their place.
  
    has_many :approved_group_members, -> { approved }, class_name: "GroupMember"
    # has_many :members, through: :approved_group_members, class_name: "Member", source: :member
    def members
      Member.where(id: self.approved_group_members.pluck(:member_id))
    end
  
    has_many :invited_group_members, -> { invited }, class_name: "GroupMember"
    # has_many :invited_members, through: :invited_group_members, class_name: "Member", source: :member
    def invited_members
      Member.where(id: self.invited_group_members.pluck(:member_id))
    end

app/models/group_member.rb

class GroupMember < ApplicationRecord
  belongs_to :group
  belongs_to :member

  enum status: { approved: 0, invited: 1, pending_approval: 2, banned: 3}
end

解决方法

现在,查看上面的代码,您应该会在组模型中看到一些有问题的解决方法。原始代码被注释掉了,下面有解决方法。代码如下:

# Many of the lines below no longer work with Multiple DBs, as we can not INNER JOIN across DBs.
# They have been commented out and methods built in their place.

has_many :approved_group_members, -> { approved }, class_name: "GroupMember"
# has_many :members, through: :approved_group_members, class_name: "Member", source: :member
def members
  Member.where(id: self.approved_group_members.pluck(:member_id))
end

has_many :invited_group_members, -> { invited }, class_name: "GroupMember"
# has_many :invited_members, through: :invited_group_members, class_name: "Member", source: :member
def invited_members
  Member.where(id: self.invited_group_members.pluck(:member_id))
end

这些变通办法的原因是,一旦 Member 类引用不同数据库上的数据,has_many 关系就无法与该类一起使用。正在构建的 SQL 查询失败。因此,我创建了一个方法,并在 Member 模型上调用了 where。这给了我正在寻找的成员名单。我必须在 9 次不同的情况下这样做。

现在我遇到的问题

在超过 100 个不同位置的代码中,我将成员或其他对象添加到给定列表中,例如使用此类代码的“受邀成员”。 (这段代码具体在我的一个规范中,现在失败了)

it 'will set the GroupMember to approved if the group is invite only and member was invited' do
  group_invitation
  group.invited_members << member
  expect { service.call! }.to change{GroupMember.count}.by(0)
  expect(service.success?).to be_truthy
  expect(service.success_message).to eq("You have successfully joined this group.")
  expect(service.error).to eq("")
end

第二行显示group.invited_members &lt;&lt; member,然后我收到一条错误消息:

NoMethodError (undefined method `<<' for #<ActiveRecord::Relation []>):

我的问题

我完全理解错误在说明什么,但不知道如何纠正它。为了让&lt;&lt; 保留下来,有没有其他方法可以解决?我非常希望只需要更改 9 次不同的时间,而不是重写 100 多个不同的实例,其中 &lt;&lt; 用于将成员或任何对象添加到被引用的列表中。

如果您需要更多详细信息,请告诉我。再次感谢!

【问题讨论】:

    标签: ruby-on-rails ruby activerecord rails-activerecord multi-database


    【解决方案1】:

    Rails 7 以上本机支持此功能。 (见https://edgeguides.rubyonrails.org/active_record_multiple_databases.html#handling-associations-with-joins-across-databaseshttps://github.blog/2021-07-12-adding-support-cross-cluster-associations-rails-7/https://github.com/rails/rails/pull/41937

    如果你真的需要这个,我可能会建议你迁移到 rails master,并使用原生方式:

    has_many :members, 
             through: :approved_group_members, 
             class_name: "Member", 
             source: :member, 
             disable_joins: true
    

    【讨论】:

    • 谢谢@rewritten,我会测试一下。我总是有点厌倦在时间紧迫的情况下进行主要版本升级,因为新升级中经常会有其他更改,然后是对应用程序有益的更改,而这些更改可能会破坏其他内容。很高兴看到这个增加的支持在 rails 6.1.4 的小更新中可用。我将从 6.1.3 更新到 6.1.4 并立即测试。谢谢。
    • 好的,将其标记为有效的答案。不过,这是我从测试中发现的。补丁没有发送到稳定分支,而是发送到主分支。因此,当更新到 6.1.4 并实施您的建议时,我收到了这个错误:Unknown key: :disable_joins。所以,我不得不用这个更新我的Gemfilegem 'rails', :github =&gt; 'rails/rails', :branch =&gt; 'main',这是 Rails 7.0.alpha。就像我想的那样,这引起了许多其他问题。我的 Bullet gem 不兼容,Devise gem 需要一个可用的补丁:gem "devise", github: "ghiculescu/devise", branch: "patch-2"
    • 一些测试仍然失败,但是这篇文章中提到的 &lt;&lt; 不工作的问题已通过在我的模型中的注释行中添加 disable_joins: true 并删除我的解决方法来解决创建的。感谢@rewritten 为我指明了正确的方向。
    【解决方案2】:
    1. 我想和rewritten一起去,你可以和轨道大师一起去。

    2. 您可以尝试将对象从 activerecord 关系转换为 活动记录收集代理对象,我们在那里 这个 方法。我认为这不是一个好方法

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-09
      • 2013-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-16
      • 1970-01-01
      相关资源
      最近更新 更多