Rails 从模型访问关联属性
Rails association attributes access from model
我有一个 rails 5 API 只有 rails 应用程序具有如下多对多关系:
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
在业务中 class 我有以下方法,我想用它来设置管理连接的所有者 table。
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
在 setter 中使用此逻辑的原因是为了管理所有没有经理、现有所有者等的情况。我发现在 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
的所有者14=] 方法 self.managers
是一个空对象。
为什么会这样,有没有一种方法可以在模型中进行更新,然后在测试中完成后保存它?
TBH 我对很多这样的代码不太满意,我真的不明白为什么我需要在 self.managements_attributes
上使用 self 以及为什么我不能只使用 managements.find_by(user_id: user.id).owner = true
来设置老板,所以如果你能给我指出一些可以在更深层次上解释关联使用的好资源,那真的很有帮助。
我认为代码可以是这样的。你写了一些关于你的代码不能正常工作的东西(所以你使用了属性)。这段代码有同样的问题吗?由于您在测试中提到的问题,我添加了很多重新加载。也许不需要。
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
要访问尚未保存的内存对象,需要进行一些更改。首先,在两侧使用 inverse_of
来 link 关联。
示例:
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
有几件事...
首先,关于business.save的测试:
来自 Rails 指南,在 "Controlling Caching":
All of the association methods are built around caching, which keeps the result of the most recent query available for further operations.
所以你必须选择:
a) 在测试中而不是 business.save
,你可以使用 business.reload
或
b) 在你的 owner=
方法中,在使用 find_by 之前重新加载你的关联:managements.reload
这里有一些关于读入测试的信息:In-Memory vs. Database Layer -- .reload
二、关于self.managements_attributes
why I can't just use managements.find_by(user_id: user.id).owner = true to set the owner
因为您没有使用该调用更新数据库中的所有者。你的来电是这样的:
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)
我有一个 rails 5 API 只有 rails 应用程序具有如下多对多关系:
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
在业务中 class 我有以下方法,我想用它来设置管理连接的所有者 table。
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
在 setter 中使用此逻辑的原因是为了管理所有没有经理、现有所有者等的情况。我发现在 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
的所有者14=] 方法 self.managers
是一个空对象。
为什么会这样,有没有一种方法可以在模型中进行更新,然后在测试中完成后保存它?
TBH 我对很多这样的代码不太满意,我真的不明白为什么我需要在 self.managements_attributes
上使用 self 以及为什么我不能只使用 managements.find_by(user_id: user.id).owner = true
来设置老板,所以如果你能给我指出一些可以在更深层次上解释关联使用的好资源,那真的很有帮助。
我认为代码可以是这样的。你写了一些关于你的代码不能正常工作的东西(所以你使用了属性)。这段代码有同样的问题吗?由于您在测试中提到的问题,我添加了很多重新加载。也许不需要。
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
要访问尚未保存的内存对象,需要进行一些更改。首先,在两侧使用 inverse_of
来 link 关联。
示例:
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
有几件事...
首先,关于business.save的测试:
来自 Rails 指南,在 "Controlling Caching":
All of the association methods are built around caching, which keeps the result of the most recent query available for further operations.
所以你必须选择:
a) 在测试中而不是 business.save
,你可以使用 business.reload
或
b) 在你的 owner=
方法中,在使用 find_by 之前重新加载你的关联:managements.reload
这里有一些关于读入测试的信息:In-Memory vs. Database Layer -- .reload
二、关于self.managements_attributes
why I can't just use managements.find_by(user_id: user.id).owner = true to set the owner
因为您没有使用该调用更新数据库中的所有者。你的来电是这样的:
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)