我有一个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
来设置所有者,所以,如果你能指出一些可以解释关联使用更好的资源的好资源,那真的会有所帮助。
答案 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)