Rails关联属性从模型

时间:2018-02-05 18:55:12

标签: ruby-on-rails associations

我有一个rails 5 API only rails app,其关系如下:

class Business < ApplicationRecord
  has_many :managements, inverse_of: :business, autosave: true
  has_many :managers, through: :managements, source: :user, autosave: true
end

class Management < ApplicationRecord
  belongs_to :user
  belongs_to :business
  validates :user, uniqueness: { scope: :business }
end

class User < ApplicationRecord
  has_many :managements
  has_many :businesses, through: :managements
end
在Business类中我有以下方法,我想用它来设置管理连接表上的所有者。

def owner=(user)
    userAsManager = managements.find_by(user_id: user.id)
    unless userAsManager
      managers << user
      self.save
      userAsManager = managements.find_by(user_id: user.id)
    end
    if userAsManager && !userAsManager.owner
      if owner
        self.managements_attributes = [
          { id: managements.find_by(user_id: owner.id).id, owner: false }
        ]
        save
      end
      self.managements_attributes = [ {id: userAsManager.id, owner: true}]
      save
    end
  end

设置器中这种逻辑的原因是管理没有管理器的所有情况,其中有现有的所有者等。我发现的是在Rspec测试中,例如:

it 'added when the owner is already a manager' do
  business.managers << owner
  business.save
  business.owner = owner
  business.save
  expect(Business.first.owner).to eq owner
  expect(Business.first.managers[0]).to eq owner
end

我必须继续保存模型,否则当我在owner=(user)方法中访问self时,相关模型不可用。例如如果未保存owner,则将business.managers设置为business,然后在输入{{{}时将所有者设置为商家business.owner = owner的所有者1}}方法owner=(user)是一个空对象。

为什么这样,有没有办法在模型中进行更新,然后在测试中保存完毕?

TBH我对很多代码并不满意,我真的不明白为什么我需要self.managers上的自我,为什么我不能只使用self.managements_attributes来设置所有者,所以,如果你能指出一些可以解释关联使用更好的资源的好资源,那真的会有所帮助。

3 个答案:

答案 0 :(得分:1)

我认为代码可能是这样的。你写了一些关于你的代码不能正常工作的东西(所以你使用了属性)。这段代码有同样的问题吗?由于您在测试中提到的问题,我添加了许多重新加载。也许他们不需要。

def owner=(user)
  #Added reload. Could this solve the problem about empty managements in tests?
  userAsManager = managements.reload.find_by(user_id: user.id)
  unless userAsManager
    managers << user
    userAsManager = managements.reload.find_by(user_id: user.id)
  end
  if userAsManager
    #Just for simplicity, remove all other owners and add this one.
    managements.update_all(owner: false)
    userAsManager.update_attributes(owner: true)
    managements.reload
  end
end

答案 1 :(得分:1)

要在内存中访问尚未保存的对象,需要进行一些更改。首先,使用双方inverse_of来链接关联。 例如:

class Business < ApplicationRecord
  has_many :managements, inverse_of: :business
end

class Management < ApplicationRecord
  belongs_to :business, inverse_of: :managements
end

要查找添加到关联但尚未保存到数据库的记录,您无法使用find_by,因为该方法会调用数据库。相反,您可以使用Array#find。像这样:

def owner=(user)
  management = managements.find { |m| m.user ==  user } || self.managments.new(user: user)
  management.owner ||= management.owner.blank?
end

答案 2 :(得分:1)

有几件事......

首先,关于测试中的business.save:

来自Rails指南,"Controlling Caching"

  

所有关联方法都是围绕缓存构建的,这使得最新查询的结果可用于进一步的操作。

所以你必须选择:

a)在business.save的测试内容中,您可以使用business.reload

b)在owner=方法中,在使用find_by之前重新加载您的关联:managements.reload

以下是关于读入测试的信息:In-Memory vs. Database Layer -- .reload

其次,关于self.managements_attributes

  

为什么我不能只使用managements.find_by(user_id:user.id).owner = true来设置所有者

因为您没有使用该调用更新数据库中的所有者。你的电话是这样的:

to_update = managements.find_by(user_id: user.id)
to_update.owner = true

这些更改不会在数据库中持久化。要更新数据库,您可以执行以下操作:

user_as_manager = managements.find_by(user_id: user.id)
user_as_manager.owner = true
user_as_manager.save # this will persist the owner to the database

相同
user_as_manager = managements.find_by(user_id: user.id)
user_as_manager.update(owner: true)

相同
managements.find_by(user_id: user.id).update(owner: true)